2003-11-13 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mcs / tools / corcompare / mono-api-diff.cs
1 //
2 // acompare.cs
3 //
4 // Authors:
5 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 //
7 // (C) 2003 Novell, Inc (http://www.novell.com)
8 //
9
10 using System;
11 using System.Collections;
12 using System.IO;
13 using System.Reflection;
14 using System.Text;
15 using System.Xml;
16
17 namespace Mono.AssemblyCompare
18 {
19         class Driver
20         {
21                 static int Main (string [] args)
22                 {
23                         if (args.Length != 2)
24                                 return 1;
25
26                         XMLAssembly ms = CreateXMLAssembly (args [0]);
27                         XMLAssembly mono = CreateXMLAssembly (args [1]);
28                         XmlDocument doc = ms.CompareAndGetDocument (mono);
29
30                         XmlTextWriter writer = new XmlTextWriter (Console.Out);
31                         writer.Formatting = Formatting.Indented;
32                         doc.WriteTo (writer);
33
34                         return 0;
35                 }
36
37                 static XMLAssembly CreateXMLAssembly (string file)
38                 {
39                         XmlDocument doc = new XmlDocument ();
40                         doc.Load (File.OpenRead (file));
41
42                         XmlNode node = doc.SelectSingleNode ("/assemblies/assembly");
43                         XMLAssembly result = new XMLAssembly ();
44                         try {
45                                 result.LoadData (node);
46                         } catch (Exception e) {
47                                 Console.Error.WriteLine ("Error loading {0}: {1}\n{2}", file, e.Message, e);
48                                 Environment.Exit (1);
49                         }
50
51                         return result;
52                 }
53         }
54
55         class Counters
56         {
57                 public int Present;
58                 public int PresentTotal;
59                 public int Missing;
60                 public int MissingTotal;
61                 public int Todo;
62                 public int TodoTotal;
63
64                 public int Extra;
65                 public int ExtraTotal;
66                 public int Warning;
67                 public int WarningTotal;
68                 public int ErrorTotal;
69
70                 public Counters ()
71                 {
72                 }
73
74                 public void AddPartialToPartial (Counters other)
75                 {
76                         Present += other.Present;
77                         Extra += other.Extra;
78                         Missing += other.Missing;
79
80                         Todo += other.Todo;
81                         Warning += other.Warning;
82                         AddPartialToTotal (other);
83                 }
84
85                 public void AddPartialToTotal (Counters other)
86                 {
87                         PresentTotal += other.Present;
88                         ExtraTotal += other.Extra;
89                         MissingTotal += other.Missing;
90
91                         TodoTotal += other.Todo;
92                         WarningTotal += other.Warning;
93                 }
94
95                 public void AddTotalToPartial (Counters other)
96                 {
97                         Present += other.PresentTotal;
98                         Extra += other.ExtraTotal;
99                         Missing += other.MissingTotal;
100
101                         Todo += other.TodoTotal;
102                         Warning += other.WarningTotal;
103                         AddTotalToTotal (other);
104                 }
105
106                 public void AddTotalToTotal (Counters other)
107                 {
108                         PresentTotal += other.PresentTotal;
109                         ExtraTotal += other.ExtraTotal;
110                         MissingTotal += other.MissingTotal;
111
112                         TodoTotal += other.TodoTotal;
113                         WarningTotal += other.WarningTotal;
114                         ErrorTotal += other.ErrorTotal;
115                 }
116
117                 public int Total {
118                         get { return Present + Missing; }
119                 }
120
121                 public int AbsTotal {
122                         get { return PresentTotal + MissingTotal; }
123                 }
124
125                 public int Ok {
126                         get { return Present - Todo; }
127                 }
128
129                 public int OkTotal {
130                         get { return PresentTotal - TodoTotal - ErrorTotal; }
131                 }
132
133                 public override string ToString ()
134                 {
135                         StringWriter sw = new StringWriter ();
136                         sw.WriteLine ("Present: {0}", Present);
137                         sw.WriteLine ("PresentTotal: {0}", PresentTotal);
138                         sw.WriteLine ("Missing: {0}", Missing);
139                         sw.WriteLine ("MissingTotal: {0}", MissingTotal);
140                         sw.WriteLine ("Todo: {0}", Todo);
141                         sw.WriteLine ("TodoTotal: {0}", TodoTotal);
142                         sw.WriteLine ("Extra: {0}", Extra);
143                         sw.WriteLine ("ExtraTotal: {0}", ExtraTotal);
144                         sw.WriteLine ("Warning: {0}", Warning);
145                         sw.WriteLine ("WarningTotal: {0}", WarningTotal);
146                         sw.WriteLine ("ErrorTotal: {0}", ErrorTotal);
147                         sw.WriteLine ("--");
148                         return sw.GetStringBuilder ().ToString ();
149                 }
150         }
151
152         abstract class XMLData
153         {
154                 protected XmlDocument document;
155                 protected Counters counters;
156                 bool haveWarnings;
157
158                 public XMLData ()
159                 {
160                         counters = new Counters ();
161                 }
162
163                 public virtual void LoadData (XmlNode node)
164                 {
165                 }
166
167                 protected object [] LoadRecursive (XmlNodeList nodeList, Type type)
168                 {
169                         ArrayList list = new ArrayList ();
170                         foreach (XmlNode node in nodeList) {
171                                 XMLData data = (XMLData) Activator.CreateInstance (type);
172                                 data.LoadData (node);
173                                 list.Add (data);
174                         }
175
176                         return (object []) list.ToArray (type);
177                 }
178
179                 protected void AddAttribute (XmlNode node, string name, string value)
180                 {
181                         XmlAttribute attr = document.CreateAttribute (name);
182                         attr.Value = value;
183                         node.Attributes.Append (attr);
184                 }
185
186                 protected void AddExtra (XmlNode node)
187                 {
188                         AddAttribute (node, "presence", "extra");
189                         AddAttribute (node, "ok", "1");
190                         AddAttribute (node, "ok_total", "1");
191                         AddAttribute (node, "extra_total", "1");
192                 }
193
194                 public void AddCountersAttributes (XmlNode node)
195                 {
196                         if (counters.Missing > 0)
197                                 AddAttribute (node, "missing", counters.Missing.ToString ());
198
199                         if (counters.Present > 0)
200                                 AddAttribute (node, "present", counters.Present.ToString ());
201
202                         if (counters.Extra > 0)
203                                 AddAttribute (node, "extra", counters.Extra.ToString ());
204
205                         if (counters.Ok > 0)
206                                 AddAttribute (node, "ok", counters.Ok.ToString ());
207
208                         if (counters.Total > 0) {
209                                 int percent = (100 * counters.Ok / counters.Total);
210                                 AddAttribute (node, "complete", percent.ToString ());
211                         }
212
213                         if (counters.Todo > 0)
214                                 AddAttribute (node, "todo", counters.Todo.ToString ());
215
216                         if (counters.Warning > 0)
217                                 AddAttribute (node, "warning", counters.Warning.ToString ());
218
219                         if (counters.MissingTotal > 0)
220                                 AddAttribute (node, "missing_total", counters.MissingTotal.ToString ());
221
222                         if (counters.PresentTotal > 0)
223                                 AddAttribute (node, "present_total", counters.PresentTotal.ToString ());
224
225                         if (counters.ExtraTotal > 0)
226                                 AddAttribute (node, "extra_total", counters.ExtraTotal.ToString ());
227
228                         if (counters.OkTotal > 0)
229                                 AddAttribute (node, "ok_total", counters.OkTotal.ToString ());
230
231                         if (counters.AbsTotal > 0) {
232                                 int percent = (100 * counters.OkTotal / counters.AbsTotal);
233                                 AddAttribute (node, "complete_total", percent.ToString ());
234                         }
235
236                         if (counters.TodoTotal > 0) {
237                                 AddAttribute (node, "todo_total", counters.TodoTotal.ToString ());
238                                 //TODO: should be different on error. check error cases in corcompare.
239                                 AddAttribute (node, "error_total", counters.Todo.ToString ());
240                         }
241
242                         if (counters.WarningTotal > 0)
243                                 AddAttribute (node, "warning_total", counters.WarningTotal.ToString ());
244
245                 }
246
247                 protected void AddWarning (XmlNode parent, string fmt, params object [] args)
248                 {
249                         counters.Warning++;
250                         haveWarnings = true;
251                         XmlNode warnings = parent.SelectSingleNode ("warnings");
252                         if (warnings == null) {
253                                 warnings = document.CreateElement ("warnings", null);
254                                 parent.AppendChild (warnings);
255                         }
256
257                         AddAttribute (parent, "error", "warning");
258                         XmlNode warning = document.CreateElement ("warnings", null);
259                         AddAttribute (warning, "text", String.Format (fmt, args));
260                         warnings.AppendChild (warning);
261                 }
262
263                 public bool HaveWarnings {
264                         get { return haveWarnings; }
265                 }
266                 
267                 public Counters Counters {
268                         get { return counters; }
269                 }
270                 
271                 public abstract void CompareTo (XmlDocument doc, XmlNode parent, object other);
272         }
273         
274         abstract class XMLNameGroup : XMLData
275         {
276                 protected XmlNode group;
277                 protected Hashtable keys;
278
279                 public override void LoadData (XmlNode node)
280                 {
281                         if (node == null)
282                                 throw new ArgumentNullException ("node");
283
284                         if (node.Name != GroupName)
285                                 throw new FormatException (String.Format ("Expecting <{0}>", GroupName));
286
287                         keys = new Hashtable ();
288                         foreach (XmlNode n in node.ChildNodes) {
289                                 string name = n.Attributes ["name"].Value;
290                                 if (CheckIfAdd (name)) {
291                                         string key = GetNodeKey (name, n);
292                                         keys.Add (key, name);
293                                         if (n.HasChildNodes)
294                                                 LoadExtraData (key, n.FirstChild);
295                                 }
296                         }
297                 }
298
299                 protected virtual bool CheckIfAdd (string value)
300                 {
301                         return true;
302                 }
303
304                 protected virtual void LoadExtraData (string name, XmlNode node)
305                 {
306                 }
307
308                 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
309                 {
310                         this.document = doc;
311                         if (group == null)
312                                 group = doc.CreateElement (GroupName, null);
313
314                         Hashtable okeys = null;
315                         if (other != null && ((XMLNameGroup) other).keys != null) {
316                                 okeys = ((XMLNameGroup) other).keys;
317                         }
318
319                         XmlNode node = null;
320                         bool onull = (okeys == null);
321                         if (keys != null) {
322                                 foreach (DictionaryEntry entry in keys) {
323                                         node = doc.CreateElement (Name, null);
324                                         group.AppendChild (node);
325                                         string key = (string) entry.Key;
326                                         string name = (string) entry.Value;
327                                         AddAttribute (node, "name", name);
328
329                                         if (!onull && HasKey (key, okeys)) {
330                                                 CompareToInner (key, node, (XMLNameGroup) other);
331                                                 okeys.Remove (key);
332                                                 counters.Present++;
333                                         } else {
334                                                 AddAttribute (node, "presence", "missing");
335                                                 counters.Missing++;
336                                         }
337                                 }
338                         }
339
340                         if (!onull && okeys.Count != 0) {
341                                 foreach (string value in okeys.Values) {
342                                         node = doc.CreateElement (Name, null);
343                                         AddAttribute (node, "name", (string) value);
344                                         AddAttribute (node, "presence", "extra");
345                                         group.AppendChild (node);
346                                         counters.Extra++;
347                                 }
348                         }
349
350                         if (group.HasChildNodes)
351                                 parent.AppendChild (group);
352                 }
353
354                 protected virtual void CompareToInner (string name, XmlNode node, XMLNameGroup other)
355                 {
356                 }
357
358                 public virtual string GetNodeKey (string name, XmlNode node)
359                 {
360                         return name;
361                 }
362
363                 public virtual bool HasKey (string key, Hashtable other)
364                 {
365                         return other.ContainsKey (key);
366                 }
367
368                 public abstract string GroupName { get; }
369                 public abstract string Name { get; }
370         }
371
372         class XMLAssembly : XMLData
373         {
374                 XMLAttributes attributes;
375                 XMLNamespace [] namespaces;
376                 string name;
377                 string version;
378
379                 public override void LoadData (XmlNode node)
380                 {
381                         if (node == null)
382                                 throw new ArgumentNullException ("node");
383
384                         name = node.Attributes ["name"].Value;
385                         version = node.Attributes  ["version"].Value;
386                         XmlNode atts = node.FirstChild;
387                         attributes = new XMLAttributes ();
388                         if (atts.Name == "attributes") {
389                                 attributes.LoadData (atts);
390                                 atts = atts.NextSibling;
391                         }
392
393                         if (atts == null || atts.Name != "namespaces") {
394                                 Console.Error.WriteLine ("Warning: no namespaces found!");
395                                 return;
396                         }
397
398                         namespaces = (XMLNamespace []) LoadRecursive (atts.ChildNodes, typeof (XMLNamespace));
399                 }
400
401                 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
402                 {
403                         XMLAssembly assembly = (XMLAssembly) other;
404
405                         XmlNode childA = doc.CreateElement ("assembly", null);
406                         AddAttribute (childA, "name", name);
407                         AddAttribute (childA, "version", version);
408                         if (name != assembly.name)
409                                 AddWarning (childA, "Assembly names not equal: {0}, {1}", name, assembly.name);
410
411                         if (version != assembly.version)
412                                 AddWarning (childA, "Assembly version not equal: {0}, {1}", version, assembly.version);
413
414                         parent.AppendChild (childA);
415
416                         attributes.CompareTo (doc, childA, assembly.attributes);
417                         counters.AddPartialToPartial (attributes.Counters);
418
419                         CompareNamespaces (childA, assembly.namespaces);
420                         if (assembly.attributes != null && assembly.attributes.IsTodo) {
421                                 counters.Todo++;
422                                 counters.TodoTotal++;
423                                 counters.ErrorTotal++;
424                                 AddAttribute (childA, "error", "todo");
425                         }
426
427                         AddCountersAttributes (childA);
428                 }
429
430                 void CompareNamespaces (XmlNode parent, XMLNamespace [] other)
431                 {
432                         ArrayList newNS = new ArrayList ();
433                         XmlNode group = document.CreateElement ("namespaces", null);
434                         parent.AppendChild (group);
435
436                         Hashtable oh = CreateHash (other);
437                         XmlNode node = null;
438                         int count = (namespaces == null) ? 0 : namespaces.Length;
439                         for (int i = 0; i < count; i++) {
440                                 XMLNamespace xns = namespaces [i];
441
442                                 node = document.CreateElement ("namespace", null);
443                                 newNS.Add (node);
444                                 AddAttribute (node, "name", xns.Name);
445
446                                 if (oh.ContainsKey (xns.Name)) {
447                                         int idx = (int) oh [xns.Name];
448                                         xns.CompareTo (document, node, other [idx]);
449                                         other [idx] = null;
450                                         xns.AddCountersAttributes (node);
451                                         counters.Present++;
452                                         counters.PresentTotal++;
453                                         counters.AddPartialToTotal (xns.Counters);
454                                 } else {
455                                         AddAttribute (node, "presence", "missing");
456                                         counters.Missing++;
457                                         counters.MissingTotal++;
458                                 }
459                         }
460
461                         if (other != null) {
462                                 count = other.Length;
463                                 for (int i = 0; i < count; i++) {
464                                         XMLNamespace n = other [i];
465                                         if (n == null)
466                                                 continue;
467
468                                         node = document.CreateElement ("namespace", null);
469                                         newNS.Add (node);
470                                         AddAttribute (node, "name", n.Name);
471                                         AddExtra (node);
472                                         counters.ExtraTotal++;
473                                 }
474                         }
475
476                         XmlNode [] nodes = (XmlNode []) newNS.ToArray (typeof (XmlNode));
477                         Array.Sort (nodes, XmlNodeComparer.Default);
478                         foreach (XmlNode nn in nodes)
479                                 group.AppendChild (nn);
480                 }
481
482                 static Hashtable CreateHash (XMLNamespace [] other)
483                 {
484                         Hashtable result = new Hashtable ();
485                         if (other != null) {
486                                 int i = 0;
487                                 foreach (XMLNamespace n in other) {
488                                         result [n.Name] = i++;
489                                 }
490                         }
491
492                         return result;
493                 }
494
495                 public XmlDocument CompareAndGetDocument (XMLAssembly other)
496                 {
497                         XmlDocument doc = new XmlDocument ();
498                         this.document = doc;
499                         XmlNode parent = doc.CreateElement ("assemblies", null);
500                         doc.AppendChild (parent);
501                         
502                         CompareTo (doc, parent, other);
503
504                         XmlNode decl = doc.CreateXmlDeclaration ("1.0", null, null);
505                         doc.InsertBefore (decl, doc.DocumentElement);
506
507                         return doc;
508                 }
509         }
510
511         class XMLNamespace : XMLData
512         {
513                 string name;
514                 XMLClass [] types;
515
516                 public override void LoadData (XmlNode node)
517                 {
518                         if (node == null)
519                                 throw new ArgumentNullException ("node");
520
521                         if (node.Name != "namespace")
522                                 throw new FormatException ("Expecting <namespace>");
523
524                         name = node.Attributes  ["name"].Value;
525                         XmlNode classes = node.FirstChild;
526                         if (classes == null) {
527                                 Console.Error.WriteLine ("Warning: no classes for {0}", node.Attributes  ["name"]);
528                                 return;
529                         }
530
531                         if (classes.Name != "classes")
532                                 throw new FormatException ("Expecting <classes>. Got <" + classes.Name + ">");
533
534                         types = (XMLClass []) LoadRecursive (classes.ChildNodes, typeof (XMLClass));
535                 }
536
537                 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
538                 {
539                         this.document = doc;
540                         XMLNamespace nspace = (XMLNamespace) other;
541
542                         XmlNode childA = doc.CreateElement ("classes", null);
543                         parent.AppendChild (childA);
544
545                         CompareTypes (childA, nspace.types);
546                 }
547
548                 void CompareTypes (XmlNode parent, XMLClass [] other)
549                 {
550                         ArrayList newNodes = new ArrayList ();
551                         Hashtable oh = CreateHash (other);
552                         XmlNode node = null;
553                         int count = (types == null) ? 0 : types.Length;
554                         for (int i = 0; i < count; i++) {
555                                 XMLClass xclass = types [i];
556
557                                 node = document.CreateElement ("class", null);
558                                 newNodes.Add (node);
559                                 AddAttribute (node, "name", xclass.Name);
560                                 AddAttribute (node, "type", xclass.Type);
561
562                                 if (oh.ContainsKey (xclass.Name)) {
563                                         int idx = (int) oh [xclass.Name];
564                                         xclass.CompareTo (document, node, other [idx]);
565                                         other [idx] = null;
566                                         counters.AddPartialToPartial (xclass.Counters);
567                                 } else {
568                                         AddAttribute (node, "presence", "missing");
569                                         counters.Missing++;
570                                         counters.MissingTotal++;
571                                 }
572                         }
573
574                         if (other != null) {
575                                 count = other.Length;
576                                 for (int i = 0; i < count; i++) {
577                                         XMLClass c = other [i];
578                                         if (c == null || c.Name == "MonoTODOAttribute")
579                                                 continue;
580
581                                         node = document.CreateElement ("class", null);
582                                         newNodes.Add (node);
583                                         AddAttribute (node, "name", c.Name);
584                                         AddExtra (node);
585                                         counters.Extra++;
586                                         counters.ExtraTotal++;
587                                 }
588                         }
589
590                         XmlNode [] nodes = (XmlNode []) newNodes.ToArray (typeof (XmlNode));
591                         Array.Sort (nodes, XmlNodeComparer.Default);
592                         foreach (XmlNode nn in nodes)
593                                 parent.AppendChild (nn);
594                 }
595
596                 static Hashtable CreateHash (XMLClass [] other)
597                 {
598                         Hashtable result = new Hashtable ();
599                         if (other != null) {
600                                 int i = 0;
601                                 foreach (XMLClass c in other) {
602                                         result [c.Name] = i++;
603                                 }
604                         }
605
606                         return result;
607                 }
608
609                 public string Name {
610                         get { return name; }
611                 }
612         }
613
614         class XMLClass : XMLData
615         {
616                 string name;
617                 string type;
618                 string baseName;
619                 bool isSealed;
620                 XMLAttributes attributes;
621                 XMLInterfaces interfaces;
622                 XMLFields fields;
623                 XMLConstructors constructors;
624                 XMLProperties properties;
625                 XMLEvents events;
626                 XMLMethods methods;
627                 XMLClass [] nested;
628                 
629                 public override void LoadData (XmlNode node)
630                 {
631                         if (node == null)
632                                 throw new ArgumentNullException ("node");
633
634                         name = node.Attributes ["name"].Value;
635                         type = node.Attributes  ["type"].Value;
636                         XmlAttribute xatt = node.Attributes ["base"];
637                         if (xatt != null)
638                                 baseName = xatt.Value;
639
640                         xatt = node.Attributes ["sealed"];
641                         isSealed = (xatt != null && xatt.Value == "true");
642
643                         XmlNode child = node.FirstChild;
644                         if (child == null) {
645                                 // Console.Error.WriteLine ("Empty class {0} {1}", name, type);
646                                 return;
647                         }
648                                 
649                         if (child.Name == "attributes") {
650                                 attributes = new XMLAttributes ();
651                                 attributes.LoadData (child);
652                                 child = child.NextSibling;
653                         }
654
655                         if (child != null && child.Name == "interfaces") {
656                                 interfaces = new XMLInterfaces ();
657                                 interfaces.LoadData (child);
658                                 child = child.NextSibling;
659                         }
660
661                         if (child != null && child.Name == "fields") {
662                                 fields = new XMLFields ();
663                                 fields.LoadData (child);
664                                 child = child.NextSibling;
665                         }
666
667                         if (child != null && child.Name == "constructors") {
668                                 constructors = new XMLConstructors ();
669                                 constructors.LoadData (child);
670                                 child = child.NextSibling;
671                         }
672
673                         if (child != null && child.Name == "properties") {
674                                 properties = new XMLProperties ();
675                                 properties.LoadData (child);
676                                 child = child.NextSibling;
677                         }
678
679                         if (child != null && child.Name == "events") {
680                                 events = new XMLEvents ();
681                                 events.LoadData (child);
682                                 child = child.NextSibling;
683                         }
684
685                         if (child != null && child.Name == "methods") {
686                                 methods = new XMLMethods ();
687                                 methods.LoadData (child);
688                                 child = child.NextSibling;
689                         }
690
691                         if (child == null)
692                                 return;
693
694                         if (child.Name != "classes") {
695                                 Console.WriteLine ("name: {0} type: {1} {2}", name, type, child.NodeType);
696                                 throw new FormatException ("Expecting <classes>. Got <" + child.Name + ">");
697                         }
698
699                         nested = (XMLClass []) LoadRecursive (child.ChildNodes, typeof (XMLClass));
700                 }
701
702                 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
703                 {
704                         this.document = doc;
705                         XMLClass oclass = (XMLClass) other;
706
707                         if (attributes != null || oclass.attributes != null) {
708                                 if (attributes == null)
709                                         attributes = new XMLAttributes ();
710
711                                 attributes.CompareTo (doc, parent, oclass.attributes);
712                                 counters.AddPartialToPartial (attributes.Counters);
713                                 if (oclass.attributes != null && oclass.attributes.IsTodo) {
714                                         counters.Todo++;
715                                         counters.TodoTotal++;
716                                         counters.ErrorTotal++;
717                                         AddAttribute (parent, "error", "todo");
718                                 }
719                         }
720
721                         if (type != oclass.type)
722                                 AddWarning (parent, "Class type is wrong: {0} != {1}", type, oclass.type);
723
724                         if (baseName != oclass.baseName)
725                                 AddWarning (parent, "Base class is wrong: {0} != {1}", baseName, oclass.baseName);
726
727                         if (isSealed != oclass.isSealed)
728                                 AddWarning (parent, "Should {0}be sealed", isSealed ? "" : "not ");
729
730                         if (interfaces != null || oclass.interfaces != null) {
731                                 if (interfaces == null)
732                                         interfaces = new XMLInterfaces ();
733
734                                 interfaces.CompareTo (doc, parent, oclass.interfaces);
735                                 counters.AddPartialToPartial (interfaces.Counters);
736                         }
737
738                         if (fields != null || oclass.fields != null) {
739                                 if (fields == null)
740                                         fields = new XMLFields ();
741
742                                 fields.CompareTo (doc, parent, oclass.fields);
743                                 counters.AddPartialToPartial (fields.Counters);
744                         }
745
746                         if (constructors != null || oclass.constructors != null) {
747                                 if (constructors == null)
748                                         constructors = new XMLConstructors ();
749
750                                 constructors.CompareTo (doc, parent, oclass.constructors);
751                                 counters.AddPartialToPartial (constructors.Counters);
752                         }
753
754                         if (properties != null || oclass.properties != null) {
755                                 if (properties == null)
756                                         properties = new XMLProperties ();
757
758                                 properties.CompareTo (doc, parent, oclass.properties);
759                                 counters.AddPartialToPartial (properties.Counters);
760                         }
761
762                         if (events != null || oclass.events != null) {
763                                 if (events == null)
764                                         events = new XMLEvents ();
765
766                                 events.CompareTo (doc, parent, oclass.events);
767                                 counters.AddPartialToPartial (events.Counters);
768                         }
769
770                         if (methods != null || oclass.methods != null) {
771                                 if (methods == null)
772                                         methods = new XMLMethods ();
773
774                                 methods.CompareTo (doc, parent, oclass.methods);
775                                 counters.AddPartialToPartial (methods.Counters);
776                         }
777
778                         if (nested != null || oclass.nested != null) {
779                                 XmlNode n = doc.CreateElement ("classes", null);
780                                 parent.AppendChild (n);
781                                 CompareTypes (n, oclass.nested);
782                         }
783
784                         AddCountersAttributes (parent);
785                 }
786
787                 void CompareTypes (XmlNode parent, XMLClass [] other)
788                 {
789                         ArrayList newNodes = new ArrayList ();
790                         Hashtable oh = CreateHash (other);
791                         XmlNode node = null;
792                         int count = (nested == null) ? 0 : nested.Length;
793                         for (int i = 0; i < count; i++) {
794                                 XMLClass xclass = nested [i];
795
796                                 node = document.CreateElement ("nestedclass", null);
797                                 newNodes.Add (node);
798                                 AddAttribute (node, "name", xclass.Name);
799                                 AddAttribute (node, "type", xclass.Type);
800
801                                 if (oh.ContainsKey (xclass.Name)) {
802                                         int idx = (int) oh [xclass.Name];
803                                         xclass.CompareTo (document, node, other [idx]);
804                                         other [idx] = null;
805                                         counters.AddPartialToPartial (xclass.Counters);
806                                 } else {
807                                         // TODO: Should I count here?
808                                         AddAttribute (node, "presence", "missing");
809                                         counters.Missing++;
810                                         counters.MissingTotal++;
811                                 }
812                         }
813
814                         if (other != null) {
815                                 count = other.Length;
816                                 for (int i = 0; i < count; i++) {
817                                         XMLClass c = other [i];
818                                         if (c == null || c.Name == "MonoTODOAttribute")
819                                                 continue;
820
821                                         node = document.CreateElement ("nestedclass", null);
822                                         newNodes.Add (node);
823                                         AddAttribute (node, "name", c.Name);
824                                         AddExtra (node);
825                                         counters.Extra++;
826                                         counters.ExtraTotal++;
827                                 }
828                         }
829
830                         XmlNode [] nodes = (XmlNode []) newNodes.ToArray (typeof (XmlNode));
831                         Array.Sort (nodes, XmlNodeComparer.Default);
832                         foreach (XmlNode nn in nodes)
833                                 parent.AppendChild (nn);
834                 }
835
836                 static Hashtable CreateHash (XMLClass [] other)
837                 {
838                         Hashtable result = new Hashtable ();
839                         if (other != null) {
840                                 int i = 0;
841                                 foreach (XMLClass c in other) {
842                                         result [c.Name] = i++;
843                                 }
844                         }
845
846                         return result;
847                 }
848
849                 public string Name {
850                         get { return name; }
851                 }
852
853                 public string Type {
854                         get { return type; }
855                 }
856         }
857
858         class XMLAttributes : XMLNameGroup
859         {
860                 bool isTodo;
861
862                 protected override bool CheckIfAdd (string value)
863                 {
864                         if (value.EndsWith (".MonoTODOAttribute")) {
865                                 isTodo = true;
866                                 return false;
867                         }
868
869                         return true;
870                 }
871
872                 public override string GetNodeKey (string name, XmlNode node)
873                 {
874                         int i = 0;
875                         while (keys.ContainsKey (name)) {
876                                 name = String.Format ("{0}:{1}", name, i++);
877                         }
878
879                         return name;
880                 }
881
882                 public override string GroupName {
883                         get { return "attributes"; }
884                 }
885
886                 public override string Name {
887                         get { return "attribute"; }
888                 }
889
890                 public bool IsTodo {
891                         get { return isTodo; }
892                 }
893         }
894
895         class XMLInterfaces : XMLNameGroup
896         {
897                 public override string GroupName {
898                         get { return "interfaces"; }
899                 }
900
901                 public override string Name {
902                         get { return "interface"; }
903                 }
904         }
905
906         abstract class XMLMember : XMLNameGroup
907         {
908                 Hashtable attributeMap;
909                 Hashtable access = new Hashtable ();
910
911                 protected override void LoadExtraData (string name, XmlNode node)
912                 {
913                         XmlAttribute xatt = node.Attributes ["attrib"];
914                         if (xatt != null)
915                                 access [name] = xatt.Value;
916                         
917                         XmlNode orig = node;
918                         while (node != null) {
919                                 if (node != null && node.Name == "attributes") {
920                                         XMLAttributes a = new XMLAttributes ();
921                                         a.LoadData (node);
922                                         if (attributeMap == null)
923                                                 attributeMap = new Hashtable ();
924
925                                         attributeMap [name] = a;
926                                         break;
927                                 }
928                                 node = node.NextSibling;
929                         }
930
931                         base.LoadExtraData (name, orig);
932                 }
933
934                 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
935                 {
936                         base.CompareToInner (name, parent, other);
937                         XMLMember mb = other as XMLMember;
938                         XMLAttributes att = null;
939                         XMLAttributes oatt = null;
940                         if (attributeMap != null)
941                                 att = attributeMap [name] as XMLAttributes;
942
943                         if (mb != null && mb.attributeMap != null)
944                                 oatt = mb.attributeMap [name] as XMLAttributes;
945
946                         if (att != null || oatt != null) {
947                                 if (att == null)
948                                         att = new XMLAttributes ();
949
950                                 att.CompareTo (document, group, oatt);
951                                 counters.AddPartialToTotal (att.Counters);
952                                 if (oatt != null && oatt.IsTodo) {
953                                         counters.Todo++;
954                                         counters.ErrorTotal++;
955                                         AddAttribute (parent, "error", "todo");
956                                 }
957                         }
958
959                         if (access == null)
960                                 return;
961
962                         XMLMember member = (XMLMember) other;
963                         string acc = access [name] as string;
964                         if (acc == null)
965                                 return;
966
967                         string oacc = null;
968                         if (member.access != null)
969                                 oacc = member.access [name] as string;
970
971                         string accName = ConvertToString (Int32.Parse (acc));
972                         string oaccName = "";
973                         if (oacc != null)
974                                 oaccName = ConvertToString (Int32.Parse (oacc));
975
976                         AddWarning (parent, "Incorrect attributes: '{0}' != '{1}'", accName, oaccName);
977                 }
978
979                 protected virtual string ConvertToString (int att)
980                 {
981                         return null;
982                 }
983         }
984         
985         class XMLFields : XMLMember
986         {
987                 Hashtable fieldTypes;
988
989                 protected override void LoadExtraData (string name, XmlNode node)
990                 {
991                         XmlAttribute xatt = node.Attributes ["fieldtype"];
992                         if (xatt != null) {
993                                 if (fieldTypes == null)
994                                         fieldTypes = new Hashtable ();
995
996                                 fieldTypes [name] = xatt.Value;
997                         }
998
999                         base.LoadExtraData (name, node);
1000                 }
1001
1002                 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1003                 {
1004                         base.CompareToInner (name, parent, other);
1005                         if (fieldTypes == null)
1006                                 return;
1007
1008                         XMLFields fields = (XMLFields) other;
1009                         string ftype = fieldTypes [name] as string;
1010                         string oftype = null;
1011                         if (fields.fieldTypes != null)
1012                                 oftype = fields.fieldTypes [name] as string;
1013
1014                         AddWarning (parent, "Field type is {0} and should be {1}", oftype, ftype);
1015                 }
1016
1017                 protected override string ConvertToString (int att)
1018                 {
1019                         FieldAttributes fa = (FieldAttributes) att;
1020                         return fa.ToString ();
1021                 }
1022
1023                 public override string GroupName {
1024                         get { return "fields"; }
1025                 }
1026
1027                 public override string Name {
1028                         get { return "field"; }
1029                 }
1030         }
1031
1032         class XMLProperties : XMLMember
1033         {
1034                 Hashtable nameToMethod = new Hashtable ();
1035
1036                 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1037                 {
1038                         XMLProperties oprop = other as XMLProperties;
1039                         if (oprop != null) {
1040                                 XMLMethods m = nameToMethod [name] as XMLMethods;
1041                                 XMLMethods om = oprop.nameToMethod [name] as XMLMethods;
1042                                 if (m != null || om != null) {
1043                                         if (m == null)
1044                                                 m = new XMLMethods ();
1045
1046                                         Counters copy = counters;
1047                                         m.CompareTo (document, parent, om);
1048                                         counters = new Counters ();
1049                                         counters.AddPartialToPartial (m.Counters);
1050                                         AddCountersAttributes (parent);
1051                                         counters = copy;
1052                                         counters.AddPartialToPartial (m.Counters);
1053                                 }
1054                         }
1055
1056                         base.CompareToInner (name, parent, other);
1057                 }
1058
1059                 protected override void LoadExtraData (string name, XmlNode node)
1060                 {
1061                         XmlNode orig = node;
1062                         while (node != null) {
1063                                 if (node != null && node.Name == "methods") {
1064                                         XMLMethods m = new XMLMethods ();
1065                                         XmlNode parent = node.ParentNode;
1066                                         string key = GetNodeKey (name, parent);
1067                                         m.LoadData (node);
1068                                         nameToMethod [key] = m;
1069                                         break;
1070                                 }
1071                                 node = node.NextSibling;
1072                         }
1073
1074                         base.LoadExtraData (name, orig);
1075                 }
1076
1077                 public override string GetNodeKey (string name, XmlNode node)
1078                 {
1079                         XmlAttributeCollection atts = node.Attributes;
1080                         return String.Format ("{0}:{1}:{2}", atts ["name"].Value,
1081                                                              atts ["ptype"].Value,
1082                                                              atts  ["params"].Value);
1083                 }
1084
1085                 public override string GroupName {
1086                         get { return "properties"; }
1087                 }
1088
1089                 public override string Name {
1090                         get { return "property"; }
1091                 }
1092         }
1093
1094         class XMLEvents : XMLMember
1095         {
1096                 Hashtable eventTypes;
1097
1098                 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
1099                 {
1100                         base.CompareTo (doc, parent, other);
1101                         AddCountersAttributes (parent);
1102                 }
1103
1104                 protected override void LoadExtraData (string name, XmlNode node)
1105                 {
1106                         XmlAttribute xatt = node.Attributes ["eventtype"];
1107                         if (xatt != null) {
1108                                 if (eventTypes == null)
1109                                         eventTypes = new Hashtable ();
1110
1111                                 eventTypes [name] = xatt.Value;
1112                         }
1113
1114                         base.LoadExtraData (name, node);
1115                 }
1116
1117                 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1118                 {
1119                         base.CompareToInner (name, parent, other);
1120                         if (eventTypes == null)
1121                                 return;
1122
1123                         XMLEvents evt = (XMLEvents) other;
1124                         string etype = eventTypes [name] as string;
1125                         string oetype = null;
1126                         if (evt.eventTypes != null)
1127                                 oetype = evt.eventTypes [name] as string;
1128
1129                         AddWarning (parent, "Event type is {0} and should be {1}", oetype, etype);
1130                 }
1131
1132                 protected override string ConvertToString (int att)
1133                 {
1134                         EventAttributes ea = (EventAttributes) att;
1135                         return ea.ToString ();
1136                 }
1137
1138                 public override string GroupName {
1139                         get { return "events"; }
1140                 }
1141
1142                 public override string Name {
1143                         get { return "event"; }
1144                 }
1145         }
1146
1147         class XMLMethods : XMLMember
1148         {
1149                 Hashtable returnTypes;
1150
1151                 protected override void LoadExtraData (string name, XmlNode node)
1152                 {
1153                         XmlAttribute xatt = node.Attributes ["returntype"];
1154                         if (xatt != null) {
1155                                 if (returnTypes == null)
1156                                         returnTypes = new Hashtable ();
1157
1158                                 returnTypes [name] = xatt.Value;
1159                         }
1160
1161                         base.LoadExtraData (name, node);
1162                 }
1163
1164                 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1165                 {
1166                         base.CompareToInner (name, parent, other);
1167                         if (returnTypes == null)
1168                                 return;
1169
1170                         XMLMethods methods = (XMLMethods) other;
1171                         string rtype = returnTypes [name] as string;
1172                         string ortype = null;
1173                         if (methods.returnTypes != null)
1174                                 ortype = methods.returnTypes [name] as string;
1175
1176                         AddWarning (parent, "Event type is {0} and should be {1}", ortype, rtype);
1177                 }
1178
1179                 protected override string ConvertToString (int att)
1180                 {
1181                         MethodAttributes ma = (MethodAttributes) att;
1182                         return ma.ToString ();
1183                 }
1184
1185                 public override string GroupName {
1186                         get { return "methods"; }
1187                 }
1188
1189                 public override string Name {
1190                         get { return "method"; }
1191                 }
1192         }
1193
1194         class XMLConstructors : XMLMethods
1195         {
1196                 public override string GroupName {
1197                         get { return "constructors"; }
1198                 }
1199
1200                 public override string Name {
1201                         get { return "constructor"; }
1202                 }
1203         }
1204
1205         class XmlNodeComparer : IComparer
1206         {
1207                 public static XmlNodeComparer Default = new XmlNodeComparer ();
1208
1209                 public int Compare (object a, object b)
1210                 {
1211                         XmlNode na = (XmlNode) a;
1212                         XmlNode nb = (XmlNode) b;
1213                         return String.Compare (na.Attributes ["name"].Value, nb.Attributes ["name"].Value);
1214                 }
1215         }
1216 }
1217