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