* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / tools / corcompare / mono-api-diff.cs
1 //
2 // mono-api-diff.cs - Compares 2 xml files produced by mono-api-info and
3 //                    produces a file suitable to build class status pages.
4 //
5 // Authors:
6 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //
8 // (C) 2003 Novell, Inc (http://www.novell.com)
9 //
10
11 using System;
12 using System.Collections;
13 using System.IO;
14 using System.Reflection;
15 using System.Text;
16 using System.Xml;
17
18 namespace Mono.AssemblyCompare
19 {
20         class Driver
21         {
22                 static int Main (string [] args)
23                 {
24                         if (args.Length != 2)
25                                 return 1;
26
27                         XMLAssembly ms = CreateXMLAssembly (args [0]);
28                         XMLAssembly mono = CreateXMLAssembly (args [1]);
29                         XmlDocument doc = ms.CompareAndGetDocument (mono);
30
31                         XmlTextWriter writer = new XmlTextWriter (Console.Out);
32                         writer.Formatting = Formatting.Indented;
33                         doc.WriteTo (writer);
34
35                         return 0;
36                 }
37
38                 static XMLAssembly CreateXMLAssembly (string file)
39                 {
40                         XmlDocument doc = new XmlDocument ();
41                         doc.Load (File.OpenRead (file));
42
43                         XmlNode node = doc.SelectSingleNode ("/assemblies/assembly");
44                         XMLAssembly result = new XMLAssembly ();
45                         try {
46                                 result.LoadData (node);
47                         } catch (Exception e) {
48                                 Console.Error.WriteLine ("Error loading {0}: {1}\n{2}", file, e.Message, e);
49                                 Environment.Exit (1);
50                         }
51
52                         return result;
53                 }
54         }
55
56         class Counters
57         {
58                 public int Present;
59                 public int PresentTotal;
60                 public int Missing;
61                 public int MissingTotal;
62                 public int Todo;
63                 public int TodoTotal;
64
65                 public int Extra;
66                 public int ExtraTotal;
67                 public int Warning;
68                 public int WarningTotal;
69                 public int ErrorTotal;
70
71                 public Counters ()
72                 {
73                 }
74
75                 public void AddPartialToPartial (Counters other)
76                 {
77                         Present += other.Present;
78                         Extra += other.Extra;
79                         Missing += other.Missing;
80
81                         Todo += other.Todo;
82                         Warning += other.Warning;
83                         AddPartialToTotal (other);
84                 }
85
86                 public void AddPartialToTotal (Counters other)
87                 {
88                         PresentTotal += other.Present;
89                         ExtraTotal += other.Extra;
90                         MissingTotal += other.Missing;
91
92                         TodoTotal += other.Todo;
93                         WarningTotal += other.Warning;
94                 }
95
96                 public void AddTotalToPartial (Counters other)
97                 {
98                         Present += other.PresentTotal;
99                         Extra += other.ExtraTotal;
100                         Missing += other.MissingTotal;
101
102                         Todo += other.TodoTotal;
103                         Warning += other.WarningTotal;
104                         AddTotalToTotal (other);
105                 }
106
107                 public void AddTotalToTotal (Counters other)
108                 {
109                         PresentTotal += other.PresentTotal;
110                         ExtraTotal += other.ExtraTotal;
111                         MissingTotal += other.MissingTotal;
112
113                         TodoTotal += other.TodoTotal;
114                         WarningTotal += other.WarningTotal;
115                         ErrorTotal += other.ErrorTotal;
116                 }
117
118                 public int Total {
119                         get { return Present + Missing; }
120                 }
121
122                 public int AbsTotal {
123                         get { return PresentTotal + MissingTotal; }
124                 }
125
126                 public int Ok {
127                         get { return Present - Todo; }
128                 }
129
130                 public int OkTotal {
131                         get { return PresentTotal - TodoTotal - ErrorTotal; }
132                 }
133
134                 public override string ToString ()
135                 {
136                         StringWriter sw = new StringWriter ();
137                         sw.WriteLine ("Present: {0}", Present);
138                         sw.WriteLine ("PresentTotal: {0}", PresentTotal);
139                         sw.WriteLine ("Missing: {0}", Missing);
140                         sw.WriteLine ("MissingTotal: {0}", MissingTotal);
141                         sw.WriteLine ("Todo: {0}", Todo);
142                         sw.WriteLine ("TodoTotal: {0}", TodoTotal);
143                         sw.WriteLine ("Extra: {0}", Extra);
144                         sw.WriteLine ("ExtraTotal: {0}", ExtraTotal);
145                         sw.WriteLine ("Warning: {0}", Warning);
146                         sw.WriteLine ("WarningTotal: {0}", WarningTotal);
147                         sw.WriteLine ("ErrorTotal: {0}", ErrorTotal);
148                         sw.WriteLine ("--");
149                         return sw.GetStringBuilder ().ToString ();
150                 }
151         }
152
153         abstract class XMLData
154         {
155                 protected XmlDocument document;
156                 protected Counters counters;
157                 bool haveWarnings;
158
159                 public XMLData ()
160                 {
161                         counters = new Counters ();
162                 }
163
164                 public virtual void LoadData (XmlNode node)
165                 {
166                 }
167
168                 protected object [] LoadRecursive (XmlNodeList nodeList, Type type)
169                 {
170                         ArrayList list = new ArrayList ();
171                         foreach (XmlNode node in nodeList) {
172                                 XMLData data = (XMLData) Activator.CreateInstance (type);
173                                 data.LoadData (node);
174                                 list.Add (data);
175                         }
176
177                         return (object []) list.ToArray (type);
178                 }
179
180                 protected void AddAttribute (XmlNode node, string name, string value)
181                 {
182                         XmlAttribute attr = document.CreateAttribute (name);
183                         attr.Value = value;
184                         node.Attributes.Append (attr);
185                 }
186
187                 protected void AddExtra (XmlNode node)
188                 {
189                         //TODO: count all the subnodes?
190                         AddAttribute (node, "presence", "extra");
191                         AddAttribute (node, "ok", "1");
192                         AddAttribute (node, "ok_total", "1");
193                         AddAttribute (node, "extra", "1");
194                         AddAttribute (node, "extra_total", "1");
195                 }
196
197                 public void AddCountersAttributes (XmlNode node)
198                 {
199                         if (counters.Missing > 0)
200                                 AddAttribute (node, "missing", counters.Missing.ToString ());
201
202                         if (counters.Present > 0)
203                                 AddAttribute (node, "present", counters.Present.ToString ());
204
205                         if (counters.Extra > 0)
206                                 AddAttribute (node, "extra", counters.Extra.ToString ());
207
208                         if (counters.Ok > 0)
209                                 AddAttribute (node, "ok", counters.Ok.ToString ());
210
211                         if (counters.Total > 0) {
212                                 int percent = (100 * counters.Ok / counters.Total);
213                                 AddAttribute (node, "complete", percent.ToString ());
214                         }
215
216                         if (counters.Todo > 0)
217                                 AddAttribute (node, "todo", counters.Todo.ToString ());
218
219                         if (counters.Warning > 0)
220                                 AddAttribute (node, "warning", counters.Warning.ToString ());
221
222                         if (counters.MissingTotal > 0)
223                                 AddAttribute (node, "missing_total", counters.MissingTotal.ToString ());
224
225                         if (counters.PresentTotal > 0)
226                                 AddAttribute (node, "present_total", counters.PresentTotal.ToString ());
227
228                         if (counters.ExtraTotal > 0)
229                                 AddAttribute (node, "extra_total", counters.ExtraTotal.ToString ());
230
231                         if (counters.OkTotal > 0)
232                                 AddAttribute (node, "ok_total", counters.OkTotal.ToString ());
233
234                         if (counters.AbsTotal > 0) {
235                                 int percent = (100 * counters.OkTotal / counters.AbsTotal);
236                                 AddAttribute (node, "complete_total", percent.ToString ());
237                         }
238
239                         if (counters.TodoTotal > 0) {
240                                 AddAttribute (node, "todo_total", counters.TodoTotal.ToString ());
241                                 //TODO: should be different on error. check error cases in corcompare.
242                                 AddAttribute (node, "error_total", counters.Todo.ToString ());
243                         }
244
245                         if (counters.WarningTotal > 0)
246                                 AddAttribute (node, "warning_total", counters.WarningTotal.ToString ());
247
248                 }
249
250                 protected void AddWarning (XmlNode parent, string fmt, params object [] args)
251                 {
252                         counters.Warning++;
253                         haveWarnings = true;
254                         XmlNode warnings = parent.SelectSingleNode ("warnings");
255                         if (warnings == null) {
256                                 warnings = document.CreateElement ("warnings", null);
257                                 parent.AppendChild (warnings);
258                         }
259
260                         AddAttribute (parent, "error", "warning");
261                         XmlNode warning = document.CreateElement ("warning", null);
262                         AddAttribute (warning, "text", String.Format (fmt, args));
263                         warnings.AppendChild (warning);
264                 }
265
266                 public bool HaveWarnings {
267                         get { return haveWarnings; }
268                 }
269                 
270                 public Counters Counters {
271                         get { return counters; }
272                 }
273                 
274                 public abstract void CompareTo (XmlDocument doc, XmlNode parent, object other);
275         }
276         
277         abstract class XMLNameGroup : XMLData
278         {
279                 protected XmlNode group;
280                 protected Hashtable keys;
281
282                 public override void LoadData (XmlNode node)
283                 {
284                         if (node == null)
285                                 throw new ArgumentNullException ("node");
286
287                         if (node.Name != GroupName)
288                                 throw new FormatException (String.Format ("Expecting <{0}>", GroupName));
289
290                         keys = new Hashtable ();
291                         foreach (XmlNode n in node.ChildNodes) {
292                                 string name = n.Attributes ["name"].Value;
293                                 if (CheckIfAdd (name, n)) {
294                                         string key = GetNodeKey (name, n);
295                                         //keys.Add (key, name);
296                                         keys [key] = name;
297                                         LoadExtraData (key, n);
298                                 }
299                         }
300                 }
301
302                 protected virtual bool CheckIfAdd (string value, XmlNode node)
303                 {
304                         return true;
305                 }
306
307                 protected virtual void LoadExtraData (string name, XmlNode node)
308                 {
309                 }
310
311                 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
312                 {
313                         this.document = doc;
314                         if (group == null)
315                                 group = doc.CreateElement (GroupName, null);
316
317                         Hashtable okeys = null;
318                         if (other != null && ((XMLNameGroup) other).keys != null) {
319                                 okeys = ((XMLNameGroup) other).keys;
320                         }
321
322                         XmlNode node = null;
323                         bool onull = (okeys == null);
324                         if (keys != null) {
325                                 foreach (DictionaryEntry entry in keys) {
326                                         node = doc.CreateElement (Name, null);
327                                         group.AppendChild (node);
328                                         string key = (string) entry.Key;
329                                         string name = (string) entry.Value;
330                                         AddAttribute (node, "name", name);
331
332                                         if (!onull && HasKey (key, okeys)) {
333                                                 CompareToInner (key, node, (XMLNameGroup) other);
334                                                 okeys.Remove (key);
335                                                 counters.Present++;
336                                         } else {
337                                                 AddAttribute (node, "presence", "missing");
338                                                 counters.Missing++;
339                                         }
340                                 }
341                         }
342
343                         if (!onull && okeys.Count != 0) {
344                                 foreach (string value in okeys.Values) {
345                                         node = doc.CreateElement (Name, null);
346                                         AddAttribute (node, "name", (string) value);
347                                         AddAttribute (node, "presence", "extra");
348                                         group.AppendChild (node);
349                                         counters.Extra++;
350                                 }
351                         }
352
353                         if (group.HasChildNodes)
354                                 parent.AppendChild (group);
355                 }
356
357                 protected virtual void CompareToInner (string name, XmlNode node, XMLNameGroup other)
358                 {
359                 }
360
361                 public virtual string GetNodeKey (string name, XmlNode node)
362                 {
363                         return name;
364                 }
365
366                 public virtual bool HasKey (string key, Hashtable other)
367                 {
368                         return other.ContainsKey (key);
369                 }
370
371                 public abstract string GroupName { get; }
372                 public abstract string Name { get; }
373         }
374
375         class XMLAssembly : XMLData
376         {
377                 XMLAttributes attributes;
378                 XMLNamespace [] namespaces;
379                 string name;
380                 string version;
381
382                 public override void LoadData (XmlNode node)
383                 {
384                         if (node == null)
385                                 throw new ArgumentNullException ("node");
386
387                         name = node.Attributes ["name"].Value;
388                         version = node.Attributes  ["version"].Value;
389                         XmlNode atts = node.FirstChild;
390                         attributes = new XMLAttributes ();
391                         if (atts.Name == "attributes") {
392                                 attributes.LoadData (atts);
393                                 atts = atts.NextSibling;
394                         }
395
396                         if (atts == null || atts.Name != "namespaces") {
397                                 Console.Error.WriteLine ("Warning: no namespaces found!");
398                                 return;
399                         }
400
401                         namespaces = (XMLNamespace []) LoadRecursive (atts.ChildNodes, typeof (XMLNamespace));
402                 }
403
404                 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
405                 {
406                         XMLAssembly assembly = (XMLAssembly) other;
407
408                         XmlNode childA = doc.CreateElement ("assembly", null);
409                         AddAttribute (childA, "name", name);
410                         AddAttribute (childA, "version", version);
411                         if (name != assembly.name)
412                                 AddWarning (childA, "Assembly names not equal: {0}, {1}", name, assembly.name);
413
414                         if (version != assembly.version)
415                                 AddWarning (childA, "Assembly version not equal: {0}, {1}", version, assembly.version);
416
417                         parent.AppendChild (childA);
418
419                         attributes.CompareTo (doc, childA, assembly.attributes);
420                         counters.AddPartialToPartial (attributes.Counters);
421
422                         CompareNamespaces (childA, assembly.namespaces);
423                         if (assembly.attributes != null && assembly.attributes.IsTodo) {
424                                 counters.Todo++;
425                                 counters.TodoTotal++;
426                                 counters.ErrorTotal++;
427                                 AddAttribute (childA, "error", "todo");
428                                 if (assembly.attributes.Comment != null)
429                                         AddAttribute (childA, "comment", assembly.attributes.Comment);
430                         }
431
432                         AddCountersAttributes (childA);
433                 }
434
435                 void CompareNamespaces (XmlNode parent, XMLNamespace [] other)
436                 {
437                         ArrayList newNS = new ArrayList ();
438                         XmlNode group = document.CreateElement ("namespaces", null);
439                         parent.AppendChild (group);
440
441                         Hashtable oh = CreateHash (other);
442                         XmlNode node = null;
443                         int count = (namespaces == null) ? 0 : namespaces.Length;
444                         for (int i = 0; i < count; i++) {
445                                 XMLNamespace xns = namespaces [i];
446
447                                 node = document.CreateElement ("namespace", null);
448                                 newNS.Add (node);
449                                 AddAttribute (node, "name", xns.Name);
450
451                                 if (oh.ContainsKey (xns.Name)) {
452                                         int idx = (int) oh [xns.Name];
453                                         xns.CompareTo (document, node, other [idx]);
454                                         other [idx] = null;
455                                         xns.AddCountersAttributes (node);
456                                         counters.Present++;
457                                         counters.PresentTotal++;
458                                         counters.AddPartialToTotal (xns.Counters);
459                                 } else {
460                                         AddAttribute (node, "presence", "missing");
461                                         counters.Missing++;
462                                         counters.MissingTotal++;
463                                 }
464                         }
465
466                         if (other != null) {
467                                 count = other.Length;
468                                 for (int i = 0; i < count; i++) {
469                                         XMLNamespace n = other [i];
470                                         if (n == null)
471                                                 continue;
472
473                                         node = document.CreateElement ("namespace", null);
474                                         newNS.Add (node);
475                                         AddAttribute (node, "name", n.Name);
476                                         AddExtra (node);
477                                         counters.ExtraTotal++;
478                                 }
479                         }
480
481                         XmlNode [] nodes = (XmlNode []) newNS.ToArray (typeof (XmlNode));
482                         Array.Sort (nodes, XmlNodeComparer.Default);
483                         foreach (XmlNode nn in nodes)
484                                 group.AppendChild (nn);
485                 }
486
487                 static Hashtable CreateHash (XMLNamespace [] other)
488                 {
489                         Hashtable result = new Hashtable ();
490                         if (other != null) {
491                                 int i = 0;
492                                 foreach (XMLNamespace n in other) {
493                                         result [n.Name] = i++;
494                                 }
495                         }
496
497                         return result;
498                 }
499
500                 public XmlDocument CompareAndGetDocument (XMLAssembly other)
501                 {
502                         XmlDocument doc = new XmlDocument ();
503                         this.document = doc;
504                         XmlNode parent = doc.CreateElement ("assemblies", null);
505                         doc.AppendChild (parent);
506                         
507                         CompareTo (doc, parent, other);
508
509                         XmlNode decl = doc.CreateXmlDeclaration ("1.0", null, null);
510                         doc.InsertBefore (decl, doc.DocumentElement);
511
512                         return doc;
513                 }
514         }
515
516         class XMLNamespace : XMLData
517         {
518                 string name;
519                 XMLClass [] types;
520
521                 public override void LoadData (XmlNode node)
522                 {
523                         if (node == null)
524                                 throw new ArgumentNullException ("node");
525
526                         if (node.Name != "namespace")
527                                 throw new FormatException ("Expecting <namespace>");
528
529                         name = node.Attributes  ["name"].Value;
530                         XmlNode classes = node.FirstChild;
531                         if (classes == null) {
532                                 Console.Error.WriteLine ("Warning: no classes for {0}", node.Attributes  ["name"]);
533                                 return;
534                         }
535
536                         if (classes.Name != "classes")
537                                 throw new FormatException ("Expecting <classes>. Got <" + classes.Name + ">");
538
539                         types = (XMLClass []) LoadRecursive (classes.ChildNodes, typeof (XMLClass));
540                 }
541
542                 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
543                 {
544                         this.document = doc;
545                         XMLNamespace nspace = (XMLNamespace) other;
546
547                         XmlNode childA = doc.CreateElement ("classes", null);
548                         parent.AppendChild (childA);
549
550                         CompareTypes (childA, nspace.types);
551                 }
552
553                 void CompareTypes (XmlNode parent, XMLClass [] other)
554                 {
555                         ArrayList newNodes = new ArrayList ();
556                         Hashtable oh = CreateHash (other);
557                         XmlNode node = null;
558                         int count = (types == null) ? 0 : types.Length;
559                         for (int i = 0; i < count; i++) {
560                                 XMLClass xclass = types [i];
561
562                                 node = document.CreateElement ("class", null);
563                                 newNodes.Add (node);
564                                 AddAttribute (node, "name", xclass.Name);
565                                 AddAttribute (node, "type", xclass.Type);
566
567                                 if (oh.ContainsKey (xclass.Name)) {
568                                         int idx = (int) oh [xclass.Name];
569                                         xclass.CompareTo (document, node, other [idx]);
570                                         other [idx] = null;
571                                         counters.AddPartialToPartial (xclass.Counters);
572                                 } else {
573                                         AddAttribute (node, "presence", "missing");
574                                         counters.Missing++;
575                                         counters.MissingTotal++;
576                                 }
577                         }
578
579                         if (other != null) {
580                                 count = other.Length;
581                                 for (int i = 0; i < count; i++) {
582                                         XMLClass c = other [i];
583                                         if (c == null || c.Name.EndsWith ("TODOAttribute"))
584                                                 continue;
585
586                                         node = document.CreateElement ("class", null);
587                                         newNodes.Add (node);
588                                         AddAttribute (node, "name", c.Name);
589                                         AddAttribute (node, "type", c.Type);
590                                         AddExtra (node);
591                                         counters.Extra++;
592                                         counters.ExtraTotal++;
593                                 }
594                         }
595
596                         XmlNode [] nodes = (XmlNode []) newNodes.ToArray (typeof (XmlNode));
597                         Array.Sort (nodes, XmlNodeComparer.Default);
598                         foreach (XmlNode nn in nodes)
599                                 parent.AppendChild (nn);
600                 }
601
602                 static Hashtable CreateHash (XMLClass [] other)
603                 {
604                         Hashtable result = new Hashtable ();
605                         if (other != null) {
606                                 int i = 0;
607                                 foreach (XMLClass c in other) {
608                                         result [c.Name] = i++;
609                                 }
610                         }
611
612                         return result;
613                 }
614
615                 public string Name {
616                         get { return name; }
617                 }
618         }
619
620         class XMLClass : XMLData
621         {
622                 string name;
623                 string type;
624                 string baseName;
625                 bool isSealed;
626                 bool isSerializable;
627                 bool isAbstract;
628                 string charSet;
629                 string layout;
630                 XMLAttributes attributes;
631                 XMLInterfaces interfaces;
632                 XMLGenericTypeConstraints genericConstraints;
633                 XMLFields fields;
634                 XMLConstructors constructors;
635                 XMLProperties properties;
636                 XMLEvents events;
637                 XMLMethods methods;
638                 XMLClass [] nested;
639                 
640                 public override void LoadData (XmlNode node)
641                 {
642                         if (node == null)
643                                 throw new ArgumentNullException ("node");
644
645                         name = node.Attributes ["name"].Value;
646                         type = node.Attributes  ["type"].Value;
647                         XmlAttribute xatt = node.Attributes ["base"];
648                         if (xatt != null)
649                                 baseName = xatt.Value;
650
651                         xatt = node.Attributes ["sealed"];
652                         isSealed = (xatt != null && xatt.Value == "true");
653
654                         xatt = node.Attributes ["abstract"];
655                         isAbstract = (xatt != null && xatt.Value == "true");
656
657                         xatt = node.Attributes["serializable"];
658                         isSerializable = (xatt != null && xatt.Value == "true");
659
660                         xatt = node.Attributes["charset"];
661                         if (xatt != null)
662                                 charSet = xatt.Value;
663
664                         xatt = node.Attributes["layout"];
665                         if (xatt != null)
666                                 layout = xatt.Value;
667
668                         XmlNode child = node.FirstChild;
669                         if (child == null) {
670                                 // Console.Error.WriteLine ("Empty class {0} {1}", name, type);
671                                 return;
672                         }
673                                 
674                         if (child.Name == "attributes") {
675                                 attributes = new XMLAttributes ();
676                                 attributes.LoadData (child);
677                                 child = child.NextSibling;
678                         }
679
680                         if (child != null && child.Name == "interfaces") {
681                                 interfaces = new XMLInterfaces ();
682                                 interfaces.LoadData (child);
683                                 child = child.NextSibling;
684                         }
685
686                         if (child != null && child.Name == "generic-type-constraints") {
687                                 genericConstraints = new XMLGenericTypeConstraints ();
688                                 genericConstraints.LoadData (child);
689                                 child = child.NextSibling;
690                         }
691
692                         if (child != null && child.Name == "fields") {
693                                 fields = new XMLFields ();
694                                 fields.LoadData (child);
695                                 child = child.NextSibling;
696                         }
697
698                         if (child != null && child.Name == "constructors") {
699                                 constructors = new XMLConstructors ();
700                                 constructors.LoadData (child);
701                                 child = child.NextSibling;
702                         }
703
704                         if (child != null && child.Name == "properties") {
705                                 properties = new XMLProperties ();
706                                 properties.LoadData (child);
707                                 child = child.NextSibling;
708                         }
709
710                         if (child != null && child.Name == "events") {
711                                 events = new XMLEvents ();
712                                 events.LoadData (child);
713                                 child = child.NextSibling;
714                         }
715
716                         if (child != null && child.Name == "methods") {
717                                 methods = new XMLMethods ();
718                                 methods.LoadData (child);
719                                 child = child.NextSibling;
720                         }
721
722                         if (child == null)
723                                 return;
724
725                         if (child.Name != "classes") {
726                                 Console.WriteLine ("name: {0} type: {1} {2}", name, type, child.NodeType);
727                                 throw new FormatException ("Expecting <classes>. Got <" + child.Name + ">");
728                         }
729
730                         nested = (XMLClass []) LoadRecursive (child.ChildNodes, typeof (XMLClass));
731                 }
732
733                 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
734                 {
735                         this.document = doc;
736                         XMLClass oclass = (XMLClass) other;
737
738                         if (attributes != null || oclass.attributes != null) {
739                                 if (attributes == null)
740                                         attributes = new XMLAttributes ();
741
742                                 attributes.CompareTo (doc, parent, oclass.attributes);
743                                 counters.AddPartialToPartial (attributes.Counters);
744                                 if (oclass.attributes != null && oclass.attributes.IsTodo) {
745                                         counters.Todo++;
746                                         counters.TodoTotal++;
747                                         counters.ErrorTotal++;
748                                         AddAttribute (parent, "error", "todo");
749                                         if (oclass.attributes.Comment != null)
750                                                 AddAttribute (parent, "comment", oclass.attributes.Comment);
751                                 }
752                         }
753
754                         if (type != oclass.type)
755                                 AddWarning (parent, "Class type is wrong: {0} != {1}", type, oclass.type);
756
757                         if (baseName != oclass.baseName)
758                                 AddWarning (parent, "Base class is wrong: {0} != {1}", baseName, oclass.baseName);
759
760                         if (isAbstract != oclass.isAbstract || isSealed != oclass.isSealed) {
761                                 if ((isAbstract && isSealed) || (oclass.isAbstract && oclass.isSealed))
762                                         AddWarning (parent, "Should {0}be static", (isAbstract && isSealed) ? "" : "not ");
763                                 else if (isAbstract != oclass.isAbstract)
764                                         AddWarning (parent, "Should {0}be abstract", isAbstract ? "" : "not ");
765                                 else if (isSealed != oclass.isSealed)
766                                         AddWarning (parent, "Should {0}be sealed", isSealed ? "" : "not ");
767                         }
768
769                         if (isSerializable != oclass.isSerializable)
770                                 AddWarning (parent, "Should {0}be serializable", isSerializable ? "" : "not ");
771
772                         if (charSet != oclass.charSet)
773                                 AddWarning (parent, "CharSet is wrong: {0} != {1}", charSet, oclass.charSet);
774
775                         if (layout != oclass.layout)
776                                 AddWarning (parent, "Layout is wrong: {0} != {1}", layout, oclass.layout);
777
778                         if (interfaces != null || oclass.interfaces != null) {
779                                 if (interfaces == null)
780                                         interfaces = new XMLInterfaces ();
781
782                                 interfaces.CompareTo (doc, parent, oclass.interfaces);
783                                 counters.AddPartialToPartial (interfaces.Counters);
784                         }
785
786                         if (genericConstraints != null || oclass.genericConstraints != null) {
787                                 if (genericConstraints == null)
788                                         genericConstraints = new XMLGenericTypeConstraints ();
789
790                                 genericConstraints.CompareTo (doc, parent, oclass.genericConstraints);
791                                 counters.AddPartialToPartial (genericConstraints.Counters);
792                         }
793
794                         if (fields != null || oclass.fields != null) {
795                                 if (fields == null)
796                                         fields = new XMLFields ();
797
798                                 fields.CompareTo (doc, parent, oclass.fields);
799                                 counters.AddPartialToPartial (fields.Counters);
800                         }
801
802                         if (constructors != null || oclass.constructors != null) {
803                                 if (constructors == null)
804                                         constructors = new XMLConstructors ();
805
806                                 constructors.CompareTo (doc, parent, oclass.constructors);
807                                 counters.AddPartialToPartial (constructors.Counters);
808                         }
809
810                         if (properties != null || oclass.properties != null) {
811                                 if (properties == null)
812                                         properties = new XMLProperties ();
813
814                                 properties.CompareTo (doc, parent, oclass.properties);
815                                 counters.AddPartialToPartial (properties.Counters);
816                         }
817
818                         if (events != null || oclass.events != null) {
819                                 if (events == null)
820                                         events = new XMLEvents ();
821
822                                 events.CompareTo (doc, parent, oclass.events);
823                                 counters.AddPartialToPartial (events.Counters);
824                         }
825
826                         if (methods != null || oclass.methods != null) {
827                                 if (methods == null)
828                                         methods = new XMLMethods ();
829
830                                 methods.CompareTo (doc, parent, oclass.methods);
831                                 counters.AddPartialToPartial (methods.Counters);
832                         }
833
834                         if (nested != null || oclass.nested != null) {
835                                 XmlNode n = doc.CreateElement ("classes", null);
836                                 parent.AppendChild (n);
837                                 CompareTypes (n, oclass.nested);
838                         }
839
840                         AddCountersAttributes (parent);
841                 }
842
843                 void CompareTypes (XmlNode parent, XMLClass [] other)
844                 {
845                         ArrayList newNodes = new ArrayList ();
846                         Hashtable oh = CreateHash (other);
847                         XmlNode node = null;
848                         int count = (nested == null) ? 0 : nested.Length;
849                         for (int i = 0; i < count; i++) {
850                                 XMLClass xclass = nested [i];
851
852                                 node = document.CreateElement ("nestedclass", null);
853                                 newNodes.Add (node);
854                                 AddAttribute (node, "name", xclass.Name);
855                                 AddAttribute (node, "type", xclass.Type);
856
857                                 if (oh.ContainsKey (xclass.Name)) {
858                                         int idx = (int) oh [xclass.Name];
859                                         xclass.CompareTo (document, node, other [idx]);
860                                         other [idx] = null;
861                                         counters.AddPartialToPartial (xclass.Counters);
862                                 } else {
863                                         // TODO: Should I count here?
864                                         AddAttribute (node, "presence", "missing");
865                                         counters.Missing++;
866                                         counters.MissingTotal++;
867                                 }
868                         }
869
870                         if (other != null) {
871                                 count = other.Length;
872                                 for (int i = 0; i < count; i++) {
873                                         XMLClass c = other [i];
874                                         if (c == null || c.Name.EndsWith ("TODOAttribute"))
875                                                 continue;
876
877                                         node = document.CreateElement ("nestedclass", null);
878                                         newNodes.Add (node);
879                                         AddAttribute (node, "name", c.Name);
880                                         AddExtra (node);
881                                         counters.Extra++;
882                                         counters.ExtraTotal++;
883                                 }
884                         }
885
886                         XmlNode [] nodes = (XmlNode []) newNodes.ToArray (typeof (XmlNode));
887                         Array.Sort (nodes, XmlNodeComparer.Default);
888                         foreach (XmlNode nn in nodes)
889                                 parent.AppendChild (nn);
890                 }
891
892                 static Hashtable CreateHash (XMLClass [] other)
893                 {
894                         Hashtable result = new Hashtable ();
895                         if (other != null) {
896                                 int i = 0;
897                                 foreach (XMLClass c in other) {
898                                         result [c.Name] = i++;
899                                 }
900                         }
901
902                         return result;
903                 }
904
905                 public string Name {
906                         get { return name; }
907                 }
908
909                 public string Type {
910                         get { return type; }
911                 }
912         }
913
914         class XMLParameter : XMLData
915         {
916                 string name;
917                 string type;
918                 string attrib;
919                 string direction;
920                 bool isUnsafe;
921                 bool isOptional;
922                 string defaultValue;
923
924                 public override void LoadData (XmlNode node)
925                 {
926                         if (node == null)
927                                 throw new ArgumentNullException ("node");
928
929                         if (node.Name != "parameter")
930                                 throw new ArgumentException ("Expecting <parameter>");
931
932                         name = node.Attributes["name"].Value;
933                         type = node.Attributes["type"].Value;
934                         attrib = node.Attributes["attrib"].Value;
935                         if (node.Attributes ["direction"] != null)
936                                 direction = node.Attributes["direction"].Value;
937                         if (node.Attributes["unsafe"] != null)
938                                 isUnsafe = bool.Parse (node.Attributes["unsafe"].Value);
939                         if (node.Attributes["optional"] != null)
940                                 isOptional = bool.Parse (node.Attributes["optional"].Value);
941                         if (node.Attributes["defaultValue"] != null)
942                                 defaultValue = node.Attributes["defaultValue"].Value;
943                 }
944
945                 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
946                 {
947                         this.document = doc;
948
949                         XMLParameter oparm = (XMLParameter) other;
950
951                         if (type != oparm.type)
952                                 AddWarning (parent, "Parameter type is wrong: {0} != {1}", type, oparm.type);
953                         
954                         if (attrib != oparm.attrib)
955                                 AddWarning (parent, "Parameter attributes wrong: {0} != {1}", attrib, oparm.attrib);
956
957                         if (direction != oparm.direction)
958                                 AddWarning (parent, "Parameter direction wrong: {0} != {1}", direction, oparm.direction);
959
960                         if (isUnsafe != oparm.isUnsafe)
961                                 AddWarning (parent, "Parameter unsafe wrong: {0} != {1}", isUnsafe, oparm.isUnsafe);
962
963                         if (isOptional != oparm.isOptional)
964                                 AddWarning (parent, "Parameter optional wrong: {0} != {1}", isOptional, oparm.isOptional);
965
966                         if (defaultValue != oparm.defaultValue)
967                                 AddWarning (parent, "Parameter default value wrong: {0} != {1}", (defaultValue == null) ? "(no default value)" : defaultValue, (oparm.defaultValue == null) ? "(no default value)" : oparm.defaultValue);
968                 }
969
970                 public string Name {
971                         get { return name; }
972                 }
973         }
974
975         class XMLAttributeProperties: XMLNameGroup
976         {
977                 static Hashtable ignored_properties;
978                 static XMLAttributeProperties ()
979                 {
980                         ignored_properties = new Hashtable ();
981                         ignored_properties.Add ("System.Reflection.AssemblyKeyFileAttribute", "KeyFile");
982                         ignored_properties.Add ("System.Reflection.AssemblyCompanyAttribute", "Company");
983                         ignored_properties.Add ("System.Reflection.AssemblyConfigurationAttribute", "Configuration");
984                         ignored_properties.Add ("System.Reflection.AssemblyCopyrightAttribute", "Copyright");
985                         ignored_properties.Add ("System.Reflection.AssemblyProductAttribute", "Product");
986                         ignored_properties.Add ("System.Reflection.AssemblyTrademarkAttribute", "Trademark");
987                         ignored_properties.Add ("System.Reflection.AssemblyInformationalVersionAttribute", "InformationalVersion");
988
989                         ignored_properties.Add ("System.ObsoleteAttribute", "Message");
990                         ignored_properties.Add ("System.IO.IODescriptionAttribute", "Description");
991                         ignored_properties.Add ("System.Diagnostics.MonitoringDescriptionAttribute", "Description");
992                 }
993
994                 Hashtable properties = new Hashtable ();
995                 string attribute;
996
997                 public XMLAttributeProperties (string attribute)
998                 {
999                         this.attribute = attribute;
1000                 }
1001
1002                 public override void LoadData(XmlNode node)
1003                 {
1004                         if (node == null)
1005                                 throw new ArgumentNullException ("node");
1006
1007                         if (node.ChildNodes == null)
1008                                 return;
1009
1010                         string ignored = ignored_properties [attribute] as string;
1011
1012                         foreach (XmlNode n in node.ChildNodes) {
1013                                 string name = n.Attributes ["name"].Value;
1014                                 if (ignored == name)
1015                                         continue;
1016
1017                                 if (n.Attributes ["null"] != null) {
1018                                         properties.Add (name, null);
1019                                         continue;
1020                                 }
1021                                 string value = n.Attributes ["value"].Value;
1022                                 properties.Add (name, value);
1023                         }
1024                 }
1025
1026                 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
1027                 {
1028                         this.document = doc;
1029
1030                         Hashtable other_properties = ((XMLAttributeProperties)other).properties;
1031                         foreach (DictionaryEntry de in other_properties) {
1032                                 object other_value = properties [de.Key];
1033
1034                                 if (de.Value == null) {
1035                                         if (other_value != null)
1036                                                 AddWarning (parent, "Property '{0}' is 'null' and should be '{1}'", de.Key, other_value);
1037                                         continue;
1038                                 }
1039
1040                                 if (de.Value.Equals (other_value))
1041                                         continue;
1042
1043                                 AddWarning (parent, "Property '{0}' is '{1}' and should be '{2}'", 
1044                                         de.Key, de.Value, other_value == null ? "null" : other_value);
1045                         }
1046                 }
1047
1048                 public override string GroupName {
1049                         get {
1050                                 return "properties";
1051                         }
1052                 }
1053
1054                 public override string Name {
1055                         get {
1056                                 return "";
1057                         }
1058                 }
1059         }
1060
1061         class XMLAttributes : XMLNameGroup
1062         {
1063                 Hashtable properties = new Hashtable ();
1064
1065                 bool isTodo;
1066                 string comment;
1067
1068                 protected override bool CheckIfAdd (string value, XmlNode node)
1069                 {
1070                         if (value.EndsWith ("TODOAttribute")) {
1071                                 isTodo = true;
1072
1073                                 XmlNode pNode = node.SelectSingleNode ("properties");
1074                                 if (pNode.ChildNodes [0].Attributes ["value"] != null) {
1075                                         comment = pNode.ChildNodes [0].Attributes ["value"].Value;
1076                                 }
1077                                 return false;
1078                         }
1079
1080                         return true;
1081                 }
1082
1083                 protected override void CompareToInner (string name, XmlNode node, XMLNameGroup other)
1084                 {
1085                         XMLAttributeProperties other_prop = ((XMLAttributes)other).properties [name] as XMLAttributeProperties;
1086                         XMLAttributeProperties this_prop = properties [name] as XMLAttributeProperties;
1087                         if (other_prop == null || this_prop == null)
1088                                 return;
1089
1090                         this_prop.CompareTo (document, node, other_prop);
1091                         counters.AddPartialToPartial (this_prop.Counters);
1092                 }
1093
1094                 public override string GetNodeKey (string name, XmlNode node)
1095                 {
1096                         string key = null;
1097
1098                         // if multiple attributes with the same name (type) exist, then we 
1099                         // cannot be sure which attributes correspond, so we must use the
1100                         // name of the attribute (type) and the name/value of its properties
1101                         // as key
1102
1103                         XmlNodeList attributes = node.ParentNode.SelectNodes("attribute[@name='" + name + "']");
1104                         if (attributes.Count > 1) {
1105                                 ArrayList keyParts = new ArrayList ();
1106
1107                                 XmlNodeList properties = node.SelectNodes ("properties/property");
1108                                 foreach (XmlNode property in properties) {
1109                                         XmlAttributeCollection attrs = property.Attributes;
1110                                         if (attrs["value"] != null) {
1111                                                 keyParts.Add (attrs["name"].Value + "=" + attrs["value"].Value);
1112                                         } else {
1113                                                 keyParts.Add (attrs["name"].Value + "=");
1114                                         }
1115                                 }
1116
1117                                 // sort properties by name, as order of properties in XML is 
1118                                 // undefined
1119                                 keyParts.Sort ();
1120
1121                                 // insert name (type) of attribute
1122                                 keyParts.Insert (0, name);
1123
1124                                 StringBuilder sb = new StringBuilder ();
1125                                 foreach (string value in keyParts) {
1126                                         sb.Append (value);
1127                                         sb.Append (';');
1128                                 }
1129                                 key = sb.ToString ();
1130                         } else {
1131                                 key = name;
1132                         }
1133
1134                         return key;
1135                 }
1136
1137                 protected override void LoadExtraData(string name, XmlNode node)
1138                 {
1139                         XmlNode pNode = node.SelectSingleNode ("properties");
1140
1141                         if (name.EndsWith ("TODOAttribute")) {
1142                                 isTodo = true;
1143                                 if (pNode.ChildNodes [0].Attributes ["value"] != null) {
1144                                         comment = pNode.ChildNodes [0].Attributes ["value"].Value;
1145                                 }
1146                                 return;
1147                         }
1148
1149                         if (pNode != null) {
1150                                 XMLAttributeProperties p = new XMLAttributeProperties (name);
1151                                 p.LoadData (pNode);
1152
1153                                 properties[name] = p;
1154                         }
1155                 }
1156
1157                 public override string GroupName {
1158                         get { return "attributes"; }
1159                 }
1160
1161                 public override string Name {
1162                         get { return "attribute"; }
1163                 }
1164
1165                 public bool IsTodo {
1166                         get { return isTodo; }
1167                 }
1168
1169                 public string Comment {
1170                         get { return comment; }
1171                 }
1172         }
1173
1174         class XMLInterfaces : XMLNameGroup
1175         {
1176                 public override string GroupName {
1177                         get { return "interfaces"; }
1178                 }
1179
1180                 public override string Name {
1181                         get { return "interface"; }
1182                 }
1183         }
1184
1185         abstract class XMLGenericGroup : XMLNameGroup
1186         {
1187                 string attributes;
1188
1189                 protected override void LoadExtraData (string name, XmlNode node)
1190                 {
1191                         attributes = ((XmlElement) node).GetAttribute ("generic-attribute");
1192                 }
1193
1194                 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1195                 {
1196                         base.CompareToInner (name, parent, other);
1197
1198                         XMLGenericGroup g = (XMLGenericGroup) other;
1199                         if (attributes != g.attributes)
1200                                 AddWarning (parent, "Incorrect generic attributes: '{0}' != '{1}'", attributes, g.attributes);
1201                 }
1202         }
1203
1204         class XMLGenericTypeConstraints : XMLGenericGroup
1205         {
1206                 public override string GroupName {
1207                         get { return "generic-type-constraints"; }
1208                 }
1209
1210                 public override string Name {
1211                         get { return "generic-type-constraint"; }
1212                 }
1213         }
1214
1215         class XMLGenericMethodConstraints : XMLGenericGroup
1216         {
1217                 public override string GroupName {
1218                         get { return "generic-method-constraints"; }
1219                 }
1220
1221                 public override string Name {
1222                         get { return "generic-method-constraint"; }
1223                 }
1224         }
1225
1226         abstract class XMLMember : XMLNameGroup
1227         {
1228                 Hashtable attributeMap;
1229                 Hashtable access = new Hashtable ();
1230
1231                 protected override void LoadExtraData (string name, XmlNode node)
1232                 {
1233                         XmlAttribute xatt = node.Attributes ["attrib"];
1234                         if (xatt != null)
1235                                 access [name] = xatt.Value;
1236                         
1237                         XmlNode orig = node;
1238
1239                         node = node.FirstChild;
1240                         while (node != null) {
1241                                 if (node != null && node.Name == "attributes") {
1242                                         XMLAttributes a = new XMLAttributes ();
1243                                         a.LoadData (node);
1244                                         if (attributeMap == null)
1245                                                 attributeMap = new Hashtable ();
1246
1247                                         attributeMap [name] = a;
1248                                         break;
1249                                 }
1250                                 node = node.NextSibling;
1251                         }
1252
1253                         base.LoadExtraData (name, orig);
1254                 }
1255
1256                 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1257                 {
1258                         base.CompareToInner (name, parent, other);
1259                         XMLMember mb = other as XMLMember;
1260                         XMLAttributes att = null;
1261                         XMLAttributes oatt = null;
1262                         if (attributeMap != null)
1263                                 att = attributeMap [name] as XMLAttributes;
1264
1265                         if (mb != null && mb.attributeMap != null)
1266                                 oatt = mb.attributeMap [name] as XMLAttributes;
1267
1268                         if (att != null || oatt != null) {
1269                                 if (att == null)
1270                                         att = new XMLAttributes ();
1271
1272                                 att.CompareTo (document, parent, oatt);
1273                                 counters.AddPartialToPartial(att.Counters);
1274                                 if (oatt != null && oatt.IsTodo) {
1275                                         counters.Todo++;
1276                                         counters.ErrorTotal++;
1277                                         AddAttribute (parent, "error", "todo");
1278                                         if (oatt.Comment != null)
1279                                                 AddAttribute (parent, "comment", oatt.Comment);
1280                                 }
1281                         }
1282
1283                         XMLMember member = (XMLMember) other;
1284                         string acc = access [name] as string;
1285                         if (acc == null)
1286                                 return;
1287
1288                         string oacc = null;
1289                         if (member.access != null)
1290                                 oacc = member.access [name] as string;
1291
1292                         string accName = ConvertToString (Int32.Parse (acc));
1293                         string oaccName = "";
1294                         if (oacc != null)
1295                                 oaccName = ConvertToString (Int32.Parse (oacc));
1296
1297                         if (accName != oaccName)
1298                                 AddWarning (parent, "Incorrect attributes: '{0}' != '{1}'", accName, oaccName);
1299                 }
1300
1301                 protected virtual string ConvertToString (int att)
1302                 {
1303                         return null;
1304                 }
1305         }
1306         
1307         class XMLFields : XMLMember
1308         {
1309                 Hashtable fieldTypes;
1310                 Hashtable fieldValues;
1311
1312                 protected override void LoadExtraData (string name, XmlNode node)
1313                 {
1314                         XmlAttribute xatt = node.Attributes ["fieldtype"];
1315                         if (xatt != null) {
1316                                 if (fieldTypes == null)
1317                                         fieldTypes = new Hashtable ();
1318
1319                                 fieldTypes [name] = xatt.Value;
1320                         }
1321
1322                         xatt = node.Attributes ["value"];
1323                         if (xatt != null) {
1324                                 if (fieldValues == null)
1325                                         fieldValues = new Hashtable ();
1326
1327                                 fieldValues[name] = xatt.Value;
1328                         }
1329
1330                         base.LoadExtraData (name, node);
1331                 }
1332
1333                 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1334                 {
1335                         base.CompareToInner (name, parent, other);
1336                         XMLFields fields = (XMLFields) other;
1337                         if (fieldTypes != null) {
1338                                 string ftype = fieldTypes [name] as string;
1339                                 string oftype = null;
1340                                 if (fields.fieldTypes != null)
1341                                         oftype = fields.fieldTypes [name] as string;
1342
1343                                 if (ftype != oftype)
1344                                         AddWarning (parent, "Field type is {0} and should be {1}", oftype, ftype);
1345                         }
1346                         if (fieldValues != null) {
1347                                 string fvalue = fieldValues [name] as string;
1348                                 string ofvalue = null;
1349                                 if (fields.fieldValues != null)
1350                                         ofvalue = fields.fieldValues [name] as string;
1351
1352                                 if (fvalue != ofvalue)
1353                                         AddWarning (parent, "Field value is {0} and should be {1}", ofvalue, fvalue);
1354                         }
1355                 }
1356
1357                 protected override string ConvertToString (int att)
1358                 {
1359                         FieldAttributes fa = (FieldAttributes) att;
1360                         return fa.ToString ();
1361                 }
1362
1363                 public override string GroupName {
1364                         get { return "fields"; }
1365                 }
1366
1367                 public override string Name {
1368                         get { return "field"; }
1369                 }
1370         }
1371
1372         class XMLParameters : XMLNameGroup
1373         {
1374                 public override void LoadData (XmlNode node)
1375                 {
1376                         if (node == null)
1377                                 throw new ArgumentNullException ("node");
1378
1379                         if (node.Name != GroupName)
1380                                 throw new FormatException (String.Format ("Expecting <{0}>", GroupName));
1381
1382                         keys = new Hashtable ();
1383                         foreach (XmlNode n in node.ChildNodes) {
1384                                 string name = n.Attributes["name"].Value;
1385                                 string key = GetNodeKey (name, n);
1386                                 XMLParameter parm = new XMLParameter ();
1387                                 parm.LoadData (n);
1388                                 keys.Add (key, parm);
1389                                 LoadExtraData (key, n);
1390                         }
1391                 }
1392
1393                 public override string GroupName {
1394                         get {
1395                                 return "parameters";
1396                         }
1397                 }
1398
1399                 public override string Name {
1400                         get {
1401                                 return "parameter";
1402                         }
1403                 }
1404
1405                 public override string GetNodeKey (string name, XmlNode node)
1406                 {
1407                         return node.Attributes["position"].Value;
1408                 }
1409
1410                 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
1411                 {
1412                         this.document = doc;
1413                         if (group == null)
1414                                 group = doc.CreateElement (GroupName, null);
1415
1416                         Hashtable okeys = null;
1417                         if (other != null && ((XMLParameters) other).keys != null) {
1418                                 okeys = ((XMLParameters) other).keys;
1419                         }
1420
1421                         XmlNode node = null;
1422                         bool onull = (okeys == null);
1423                         if (keys != null) {
1424                                 foreach (DictionaryEntry entry in keys) {
1425                                         node = doc.CreateElement (Name, null);
1426                                         group.AppendChild (node);
1427                                         string key = (string) entry.Key;
1428                                         XMLParameter parm = (XMLParameter) entry.Value;
1429                                         AddAttribute (node, "name", parm.Name);
1430
1431                                         if (!onull && HasKey (key, okeys)) {
1432                                                 parm.CompareTo (document, node, okeys[key]);
1433                                                 counters.AddPartialToPartial (parm.Counters);
1434                                                 okeys.Remove (key);
1435                                                 counters.Present++;
1436                                         } else {
1437                                                 AddAttribute (node, "presence", "missing");
1438                                                 counters.Missing++;
1439                                         }
1440                                 }
1441                         }
1442
1443                         if (!onull && okeys.Count != 0) {
1444                                 foreach (XMLParameter value in okeys.Values) {
1445                                         node = doc.CreateElement (Name, null);
1446                                         AddAttribute (node, "name", value.Name);
1447                                         AddAttribute (node, "presence", "extra");
1448                                         group.AppendChild (node);
1449                                         counters.Extra++;
1450                                 }
1451                         }
1452
1453                         if (group.HasChildNodes)
1454                                 parent.AppendChild (group);
1455                 }
1456         }
1457
1458         class XMLProperties : XMLMember
1459         {
1460                 Hashtable nameToMethod = new Hashtable ();
1461
1462                 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1463                 {
1464                         Counters copy = counters;
1465                         counters = new Counters();
1466
1467                         XMLProperties oprop = other as XMLProperties;
1468                         if (oprop != null) {
1469                                 XMLMethods m = nameToMethod [name] as XMLMethods;
1470                                 XMLMethods om = oprop.nameToMethod [name] as XMLMethods;
1471                                 if (m != null || om != null) {
1472                                         if (m == null)
1473                                                 m = new XMLMethods ();
1474
1475                                         m.CompareTo(document, parent, om);
1476                                         counters.AddPartialToPartial(m.Counters);
1477                                 }
1478                         }
1479
1480                         base.CompareToInner (name, parent, other);
1481                         AddCountersAttributes(parent);
1482
1483                         copy.AddPartialToPartial(counters);
1484                         counters = copy;
1485                 }
1486
1487                 protected override void LoadExtraData (string name, XmlNode node)
1488                 {
1489                         XmlNode orig = node;
1490                         node = node.FirstChild;
1491                         while (node != null) {
1492                                 if (node != null && node.Name == "methods") {
1493                                         XMLMethods m = new XMLMethods ();
1494                                         XmlNode parent = node.ParentNode;
1495                                         string key = GetNodeKey (name, parent);
1496                                         m.LoadData (node);
1497                                         nameToMethod [key] = m;
1498                                         break;
1499                                 }
1500                                 node = node.NextSibling;
1501                         }
1502
1503                         base.LoadExtraData (name, orig);
1504                 }
1505
1506                 public override string GetNodeKey (string name, XmlNode node)
1507                 {
1508                         XmlAttributeCollection atts = node.Attributes;
1509                         return String.Format ("{0}:{1}:{2}", atts ["name"].Value,
1510                                                                 atts ["ptype"].Value,
1511                                                                 atts ["params"].Value);
1512                 }
1513
1514                 public override string GroupName {
1515                         get { return "properties"; }
1516                 }
1517
1518                 public override string Name {
1519                         get { return "property"; }
1520                 }
1521         }
1522
1523         class XMLEvents : XMLMember
1524         {
1525                 Hashtable eventTypes;
1526
1527                 protected override void LoadExtraData (string name, XmlNode node)
1528                 {
1529                         XmlAttribute xatt = node.Attributes ["eventtype"];
1530                         if (xatt != null) {
1531                                 if (eventTypes == null)
1532                                         eventTypes = new Hashtable ();
1533
1534                                 eventTypes [name] = xatt.Value;
1535                         }
1536
1537                         base.LoadExtraData (name, node);
1538                 }
1539
1540                 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1541                 {
1542                         Counters copy = counters;
1543                         counters = new Counters ();
1544
1545                         try {
1546                                 base.CompareToInner (name, parent, other);
1547                                 AddCountersAttributes (parent);
1548                                 if (eventTypes == null)
1549                                         return;
1550
1551                                 XMLEvents evt = (XMLEvents) other;
1552                                 string etype = eventTypes [name] as string;
1553                                 string oetype = null;
1554                                 if (evt.eventTypes != null)
1555                                         oetype = evt.eventTypes [name] as string;
1556
1557                                 if (etype != oetype)
1558                                         AddWarning (parent, "Event type is {0} and should be {1}", oetype, etype);
1559                         } finally {
1560                                 AddCountersAttributes (parent);
1561                                 copy.AddPartialToPartial (counters);
1562                                 counters = copy;
1563                         }
1564                 }
1565
1566                 protected override string ConvertToString (int att)
1567                 {
1568                         EventAttributes ea = (EventAttributes) att;
1569                         return ea.ToString ();
1570                 }
1571
1572                 public override string GroupName {
1573                         get { return "events"; }
1574                 }
1575
1576                 public override string Name {
1577                         get { return "event"; }
1578                 }
1579         }
1580
1581         class XMLMethods : XMLMember
1582         {
1583                 Hashtable returnTypes;
1584                 Hashtable parameters;
1585                 Hashtable genericConstraints;
1586                 Hashtable signatureFlags;
1587
1588                 [Flags]
1589                 enum SignatureFlags
1590                 {
1591                         None = 0,
1592                         Abstract = 1,
1593                         Virtual = 2,
1594                         Static = 4
1595                 }
1596
1597                 protected override void LoadExtraData (string name, XmlNode node)
1598                 {
1599                         XmlAttribute xatt = node.Attributes ["returntype"];
1600                         if (xatt != null) {
1601                                 if (returnTypes == null)
1602                                         returnTypes = new Hashtable ();
1603
1604                                 returnTypes [name] = xatt.Value;
1605                         }
1606
1607                         SignatureFlags flags = SignatureFlags.None;
1608                         if (((XmlElement) node).GetAttribute ("abstract") == "true")
1609                                 flags |= SignatureFlags.Abstract;
1610                         if (((XmlElement) node).GetAttribute ("static") == "true")
1611                                 flags |= SignatureFlags.Static;
1612                         if (((XmlElement) node).GetAttribute ("virtual") == "true")
1613                                 flags |= SignatureFlags.Virtual;
1614                         if (flags != SignatureFlags.None) {
1615                                 if (signatureFlags == null)
1616                                         signatureFlags = new Hashtable ();
1617                                 signatureFlags [name] = flags;
1618                         }
1619
1620                         XmlNode parametersNode = node.SelectSingleNode ("parameters");
1621                         if (parametersNode != null) {
1622                                 if (parameters == null)
1623                                         parameters = new Hashtable ();
1624
1625                                 XMLParameters parms = new XMLParameters ();
1626                                 parms.LoadData (parametersNode);
1627
1628                                 parameters[name] = parms;
1629                         }
1630
1631                         XmlNode genericNode = node.SelectSingleNode ("generic-method-constraints");
1632                         if (genericNode != null) {
1633                                 if (genericConstraints == null)
1634                                         genericConstraints = new Hashtable ();
1635                                 XMLGenericMethodConstraints csts = new XMLGenericMethodConstraints ();
1636                                 csts.LoadData (genericNode);
1637                                 genericConstraints [name] = csts;
1638                         }
1639
1640                         base.LoadExtraData (name, node);
1641                 }
1642
1643                 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1644                 {
1645                         // create backup of actual counters
1646                         Counters copy = counters;
1647                         // initialize counters for current method
1648                         counters = new Counters();
1649
1650                         try {
1651                                 base.CompareToInner(name, parent, other);
1652                                 XMLMethods methods = (XMLMethods) other;
1653
1654                                 SignatureFlags flags = signatureFlags != null &&
1655                                         signatureFlags.ContainsKey (name) ?
1656                                         (SignatureFlags) signatureFlags [name] :
1657                                         SignatureFlags.None;
1658                                 SignatureFlags oflags = methods.signatureFlags != null &&
1659                                         methods.signatureFlags.ContainsKey (name) ?
1660                                         (SignatureFlags) methods.signatureFlags [name] :
1661                                         SignatureFlags.None;
1662
1663                                 if (flags!= oflags) {
1664                                         if (flags == SignatureFlags.None)
1665                                                 AddWarning (parent, String.Format ("should not be {0}", oflags));
1666                                         else if (oflags == SignatureFlags.None)
1667                                                 AddWarning (parent, String.Format ("should be {0}", flags));
1668                                         else
1669                                                 AddWarning (parent, String.Format ("{0} and should be {1}", oflags, flags));
1670                                 }
1671
1672                                 if (returnTypes != null) {
1673                                         string rtype = returnTypes[name] as string;
1674                                         string ortype = null;
1675                                         if (methods.returnTypes != null)
1676                                                 ortype = methods.returnTypes[name] as string;
1677
1678                                         if (rtype != ortype)
1679                                                 AddWarning (parent, "Return type is {0} and should be {1}", ortype, rtype);
1680                                 }
1681
1682                                 if (parameters != null) {
1683                                         XMLParameters parms = parameters[name] as XMLParameters;
1684                                         parms.CompareTo (document, parent, methods.parameters[name]);
1685                                         counters.AddPartialToPartial (parms.Counters);
1686                                 }
1687                         } finally {
1688                                 // output counter attributes in result document
1689                                 AddCountersAttributes(parent);
1690
1691                                 // add temporary counters to actual counters
1692                                 copy.AddPartialToPartial(counters);
1693                                 // restore backup of actual counters
1694                                 counters = copy;
1695                         }
1696                 }
1697
1698                 protected override string ConvertToString (int att)
1699                 {
1700                         MethodAttributes ma = (MethodAttributes) att;
1701                         // ignore ReservedMasks
1702                         ma &= ~ MethodAttributes.ReservedMask;
1703                         ma &= ~ MethodAttributes.VtableLayoutMask;
1704                         if ((ma & MethodAttributes.FamORAssem) != 0)
1705                                 ma = (ma & ~ MethodAttributes.FamORAssem) | MethodAttributes.Family;
1706
1707                         // ignore the HasSecurity attribute for now
1708                         if ((ma & MethodAttributes.HasSecurity) != 0)
1709                                 ma = (MethodAttributes) (att - (int) MethodAttributes.HasSecurity);
1710
1711                         // ignore the RequireSecObject attribute for now
1712                         if ((ma & MethodAttributes.RequireSecObject) != 0)
1713                                 ma = (MethodAttributes) (att - (int) MethodAttributes.RequireSecObject);
1714
1715                         // we don't care if the implementation is forwarded through PInvoke 
1716                         if ((ma & MethodAttributes.PinvokeImpl) != 0)
1717                                 ma = (MethodAttributes) (att - (int) MethodAttributes.PinvokeImpl);
1718
1719                         return ma.ToString ();
1720                 }
1721
1722                 public override string GroupName {
1723                         get { return "methods"; }
1724                 }
1725
1726                 public override string Name {
1727                         get { return "method"; }
1728                 }
1729         }
1730
1731         class XMLConstructors : XMLMethods
1732         {
1733                 public override string GroupName {
1734                         get { return "constructors"; }
1735                 }
1736
1737                 public override string Name {
1738                         get { return "constructor"; }
1739                 }
1740         }
1741
1742         class XmlNodeComparer : IComparer
1743         {
1744                 public static XmlNodeComparer Default = new XmlNodeComparer ();
1745
1746                 public int Compare (object a, object b)
1747                 {
1748                         XmlNode na = (XmlNode) a;
1749                         XmlNode nb = (XmlNode) b;
1750                         return String.Compare (na.Attributes ["name"].Value, nb.Attributes ["name"].Value);
1751                 }
1752         }
1753 }
1754