internal static void generate(String xml1, String xml2, String title, String part1, String part2, String returnstext, String propertytext, String outfilename)
        {
            objects = new Dictionary <String, EV3Object>();

            // read documentation for the EV3 extension
            ReadFile(xml1);
            // read documentation for the small basic classes
            ReadFile(xml2);
            // remove unsupported objectgs
            objects.Remove("EV3Communicator");
            objects.Remove("Resources");
            objects.Remove("Turtle");
            objects.Remove("File");
            objects.Remove("Primitive");
            objects.Remove("Array");
            objects.Remove("NativeHelper");
            objects.Remove("Desktop");
            objects.Remove("GraphicsWindow");
            objects.Remove("Sound");
            objects.Remove("Keywords");
            objects.Remove("Mouse");
            objects.Remove("Flickr");
            objects.Remove("RestHelper");
            objects.Remove("Timer");
            objects.Remove("Platform");
            objects.Remove("Stack");
            objects.Remove("Dictionary");
            objects.Remove("Clock");
            objects.Remove("Network");
            objects.Remove("SmallBasicCallback");
            objects.Remove("SmallBasicApplication");
            objects.Remove("ImageList");
            objects.Remove("Controls");
            objects.Remove("Shapes");
            objects.Remove("TextWindow");

            // write documentation file
            FileStream   fs     = new FileStream(outfilename, FileMode.Create, FileAccess.Write);
            StreamWriter target = new StreamWriter(fs, new UTF8Encoding(false));

            target.WriteLine("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">");
            target.WriteLine("<HTML>");
            target.WriteLine("<HEAD>");
            target.WriteLine("<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\" >");
            target.WriteLine("<TITLE>" + title + "</TITLE>");
            target.WriteLine(Documentation.Properties.Resources.Styles);
            target.WriteLine("</HEAD>");
            target.WriteLine("<BODY>");

            target.WriteLine(part1);

            // add API documentation
            var list = objects.Keys.ToList();

            list.Sort();

            foreach (var opkey in list)
            {
                var o = objects[opkey];
                target.WriteLine("<H2 class=\"object\">" + opkey + "</H2>");
                target.WriteLine("<P class=\"objectsummary\">" + o.summary.Replace("\n", "<BR>").Replace("<>", "&lt;&gt;") + "</P>");

                // write properties
                var plist = o.properties.Keys.ToList();
                plist.Sort();
                foreach (var pkey in plist)
                {
                    String p = o.properties[pkey];
                    target.WriteLine("<H3 class=\"property\">" + opkey + "." + pkey + " - " + propertytext + "</H3>");
                    target.WriteLine("<P class=\"propertysummary\">" + p.Replace("\n", "<BR>").Replace("<>", "&lt;&gt;") + "</P>");
                }
                // write functions
                var flist = o.functions.Keys.ToList();
                flist.Sort();
                foreach (var fkey in flist)
                {
                    // skip certain functions to not fill up the documentation
                    if (opkey.Equals("F") && fkey.StartsWith("Call") && !fkey.Equals("Call0") && !fkey.Equals("Call1") && !fkey.Equals("Call2"))
                    {
                        continue;
                    }
                    EV3Function f = o.functions[fkey];
                    target.WriteLine("<H3 class=\"operation\">" + opkey + "." + fkey + " " + f.GetParameterList() + "</H3>");
                    target.WriteLine("<P class=\"operationsummary\">" + f.summary.Replace("\n", "<BR>").Replace("<>", "&lt;&gt;") + "</P>");

                    // write function parameters
                    foreach (var pp in f.parameters)
                    {
                        String p = pp.Value;
                        target.WriteLine("<H4 class=\"parameter\">" + pp.Key + "</H4>");
                        target.WriteLine("<P class=\"parametersummary\">" + p.Replace("<>", "&lt;&gt;") + "</P>");
                    }
                    // write return value
                    if (f.returnvalue != null)
                    {
                        target.WriteLine("<H4 class=\"returns\">" + returnstext + "</H4>");
                        target.WriteLine("<P class=\"returnssummary\">" + f.returnvalue.Replace("<>", "&lt;&gt;") + "</P>");
                    }
                }
            }

            target.WriteLine(part2);

            target.WriteLine("</BODY>");
            target.WriteLine("</HTML>");

            target.Close();

//            Console.ReadLine();
        }
        internal static void ReadFile(String filename)
        {
            // parse all data from the XML files
            XmlDocument xmlDoc = new XmlDocument();

            xmlDoc.Load(filename);

            // scan all 'member' nodes
            foreach (XmlNode member in xmlDoc.ChildNodes[1].SelectNodes("members/member"))
            {
                // only consider members that have a summary
                XmlNode summary = member.SelectSingleNode("summary");
                if (summary != null)
                {
                    String summarystring = TrimIndents(summary.InnerText.Trim());

                    // extract name (without parameter list)  and  get member type
                    String name   = member.Attributes["name"].Value;
                    char   type   = name[0];
                    int    paridx = name.IndexOf('(');
                    if (paridx >= 0)
                    {
                        name = name.Substring(0, paridx);
                    }
                    if (type == 'T')
                    {
                        int dotidx = name.LastIndexOf('.');
                        if (dotidx >= 0)
                        {
                            name = name.Substring(dotidx + 1);
                        }
                    }
                    else
                    {
                        int dotidx = name.LastIndexOf('.');
                        dotidx = name.LastIndexOf('.', dotidx - 1);
                        if (dotidx >= 0)
                        {
                            name = name.Substring(dotidx + 1);
                        }
                    }

                    switch (type)
                    {
                    case 'T':
                        objects[name] = new EV3Object(summarystring);
                        break;

                    case 'M':
                        if (!name.EndsWith("DoubleToDecimal"))          // do not document this internal function
                        {
                            int         dotidx = name.IndexOf('.');
                            EV3Function f      = new EV3Function(summarystring);
                            objects[name.Substring(0, dotidx)].functions[name.Substring(dotidx + 1)] = f;
                            foreach (XmlNode param in member.SelectNodes("param"))
                            {
                                //                                Console.WriteLine("PAR: "+param.Attributes["name"].Value);
                                //                                Console.WriteLine("INFO: "+TrimIndents(param.InnerText.Trim()));
                                f.parameters[param.Attributes["name"].Value] = TrimIndents(param.InnerText.Trim());
                            }
                            XmlNode returnvalue = member.SelectSingleNode("returns");
                            if (returnvalue != null)
                            {
                                f.returnvalue = TrimIndents(returnvalue.InnerText.Trim());
                            }
                        }
                        break;

                    case 'E':
                    case 'P':
                    {
                        int dotidx = name.IndexOf('.');
                        objects[name.Substring(0, dotidx)].properties[name.Substring(dotidx + 1)] = summarystring;
                    }
                    break;
                    }
                }
            }
        }