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