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