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