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.
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // Marek Safar (marek.safar@gmail.com)
9 // (C) 2003 Novell, Inc (http://www.novell.com)
13 using System.Collections;
15 using System.Reflection;
19 namespace Mono.AssemblyCompare
23 static int Main (string [] args)
25 if (args.Length != 2) {
26 Console.WriteLine ("Usage: mono mono-api-diff.exe <assembly 1 xml> <assembly 2 xml>");
30 XMLAssembly ms = CreateXMLAssembly (args [0]);
31 XMLAssembly mono = CreateXMLAssembly (args [1]);
32 XmlDocument doc = ms.CompareAndGetDocument (mono);
34 XmlTextWriter writer = new XmlTextWriter (Console.Out);
35 writer.Formatting = Formatting.Indented;
41 static XMLAssembly CreateXMLAssembly (string file)
43 XmlDocument doc = new XmlDocument ();
44 doc.Load (File.OpenRead (file));
46 XmlNode node = doc.SelectSingleNode ("/assemblies/assembly");
47 XMLAssembly result = new XMLAssembly ();
49 result.LoadData (node);
50 } catch (Exception e) {
51 Console.Error.WriteLine ("Error loading {0}: {1}\n{2}", file, e.Message, e);
62 public int PresentTotal;
64 public int MissingTotal;
69 public int ExtraTotal;
71 public int WarningTotal;
72 public int ErrorTotal;
78 public void AddPartialToPartial (Counters other)
80 Present += other.Present;
82 Missing += other.Missing;
85 Warning += other.Warning;
86 AddPartialToTotal (other);
89 public void AddPartialToTotal (Counters other)
91 PresentTotal += other.Present;
92 ExtraTotal += other.Extra;
93 MissingTotal += other.Missing;
95 TodoTotal += other.Todo;
96 WarningTotal += other.Warning;
99 public void AddTotalToPartial (Counters other)
101 Present += other.PresentTotal;
102 Extra += other.ExtraTotal;
103 Missing += other.MissingTotal;
105 Todo += other.TodoTotal;
106 Warning += other.WarningTotal;
107 AddTotalToTotal (other);
110 public void AddTotalToTotal (Counters other)
112 PresentTotal += other.PresentTotal;
113 ExtraTotal += other.ExtraTotal;
114 MissingTotal += other.MissingTotal;
116 TodoTotal += other.TodoTotal;
117 WarningTotal += other.WarningTotal;
118 ErrorTotal += other.ErrorTotal;
122 get { return Present + Missing; }
125 public int AbsTotal {
126 get { return PresentTotal + MissingTotal; }
130 get { return Present - Todo; }
134 get { return PresentTotal - TodoTotal - ErrorTotal; }
137 public override string ToString ()
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);
152 return sw.GetStringBuilder ().ToString ();
156 abstract class XMLData
158 protected XmlDocument document;
159 protected Counters counters;
164 counters = new Counters ();
167 public virtual void LoadData (XmlNode node)
171 protected object [] LoadRecursive (XmlNodeList nodeList, Type type)
173 ArrayList list = new ArrayList ();
174 foreach (XmlNode node in nodeList) {
175 XMLData data = (XMLData) Activator.CreateInstance (type);
176 data.LoadData (node);
180 return (object []) list.ToArray (type);
183 public static bool IsMeaninglessAttribute (string s)
187 if (s == "System.Runtime.CompilerServices.CompilerGeneratedAttribute")
192 public static bool IsMonoTODOAttribute (string s)
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"))
203 return s.EndsWith ("TODOAttribute");
206 protected void AddAttribute (XmlNode node, string name, string value)
208 XmlAttribute attr = document.CreateAttribute (name);
210 node.Attributes.Append (attr);
213 protected void AddExtra (XmlNode node)
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");
223 public void AddCountersAttributes (XmlNode node)
225 if (counters.Missing > 0)
226 AddAttribute (node, "missing", counters.Missing.ToString ());
228 if (counters.Present > 0)
229 AddAttribute (node, "present", counters.Present.ToString ());
231 if (counters.Extra > 0)
232 AddAttribute (node, "extra", counters.Extra.ToString ());
235 AddAttribute (node, "ok", counters.Ok.ToString ());
237 if (counters.Total > 0) {
238 int percent = (100 * counters.Ok / counters.Total);
239 AddAttribute (node, "complete", percent.ToString ());
242 if (counters.Todo > 0)
243 AddAttribute (node, "todo", counters.Todo.ToString ());
245 if (counters.Warning > 0)
246 AddAttribute (node, "warning", counters.Warning.ToString ());
248 if (counters.MissingTotal > 0)
249 AddAttribute (node, "missing_total", counters.MissingTotal.ToString ());
251 if (counters.PresentTotal > 0)
252 AddAttribute (node, "present_total", counters.PresentTotal.ToString ());
254 if (counters.ExtraTotal > 0)
255 AddAttribute (node, "extra_total", counters.ExtraTotal.ToString ());
257 if (counters.OkTotal > 0)
258 AddAttribute (node, "ok_total", counters.OkTotal.ToString ());
260 if (counters.AbsTotal > 0) {
261 int percent = (100 * counters.OkTotal / counters.AbsTotal);
262 AddAttribute (node, "complete_total", percent.ToString ());
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 ());
271 if (counters.WarningTotal > 0)
272 AddAttribute (node, "warning_total", counters.WarningTotal.ToString ());
276 protected void AddWarning (XmlNode parent, string fmt, params object [] args)
280 XmlNode warnings = parent.SelectSingleNode ("warnings");
281 if (warnings == null) {
282 warnings = document.CreateElement ("warnings", null);
283 parent.AppendChild (warnings);
286 AddAttribute (parent, "error", "warning");
287 XmlNode warning = document.CreateElement ("warning", null);
288 AddAttribute (warning, "text", String.Format (fmt, args));
289 warnings.AppendChild (warning);
292 public bool HaveWarnings {
293 get { return haveWarnings; }
296 public Counters Counters {
297 get { return counters; }
300 public abstract void CompareTo (XmlDocument doc, XmlNode parent, object other);
303 abstract class XMLNameGroup : XMLData
305 protected XmlNode group;
306 protected Hashtable keys;
308 public override void LoadData (XmlNode node)
311 throw new ArgumentNullException ("node");
313 if (node.Name != GroupName)
314 throw new FormatException (String.Format ("Expecting <{0}>", GroupName));
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);
323 LoadExtraData (key, n);
328 protected virtual bool CheckIfAdd (string value, XmlNode node)
333 protected virtual void LoadExtraData (string name, XmlNode node)
337 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
341 group = doc.CreateElement (GroupName, null);
343 Hashtable okeys = null;
344 if (other != null && ((XMLNameGroup) other).keys != null) {
345 okeys = ((XMLNameGroup) other).keys;
349 bool onull = (okeys == 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);
358 if (!onull && HasKey (key, okeys)) {
359 CompareToInner (key, node, (XMLNameGroup) other);
363 AddAttribute (node, "presence", "missing");
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);
379 if (group.HasChildNodes)
380 parent.AppendChild (group);
383 protected virtual void CompareToInner (string name, XmlNode node, XMLNameGroup other)
387 public virtual string GetNodeKey (string name, XmlNode node)
392 public virtual bool HasKey (string key, Hashtable other)
394 return other.ContainsKey (key);
397 public abstract string GroupName { get; }
398 public abstract string Name { get; }
401 class XMLAssembly : XMLData
403 XMLAttributes attributes;
404 XMLNamespace [] namespaces;
408 public override void LoadData (XmlNode node)
411 throw new ArgumentNullException ("node");
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;
422 if (atts == null || atts.Name != "namespaces") {
423 Console.Error.WriteLine ("Warning: no namespaces found!");
427 namespaces = (XMLNamespace []) LoadRecursive (atts.ChildNodes, typeof (XMLNamespace));
430 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
432 XMLAssembly assembly = (XMLAssembly) other;
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);
440 if (version != assembly.version)
441 AddWarning (childA, "Assembly version not equal: {0}, {1}", version, assembly.version);
443 parent.AppendChild (childA);
445 attributes.CompareTo (doc, childA, assembly.attributes);
446 counters.AddPartialToPartial (attributes.Counters);
448 CompareNamespaces (childA, assembly.namespaces);
449 if (assembly.attributes != null && assembly.attributes.IsTodo) {
451 counters.TodoTotal++;
452 counters.ErrorTotal++;
453 AddAttribute (childA, "error", "todo");
454 if (assembly.attributes.Comment != null)
455 AddAttribute (childA, "comment", assembly.attributes.Comment);
458 AddCountersAttributes (childA);
461 void CompareNamespaces (XmlNode parent, XMLNamespace [] other)
463 ArrayList newNS = new ArrayList ();
464 XmlNode group = document.CreateElement ("namespaces", null);
465 parent.AppendChild (group);
467 Hashtable oh = CreateHash (other);
469 int count = (namespaces == null) ? 0 : namespaces.Length;
470 for (int i = 0; i < count; i++) {
471 XMLNamespace xns = namespaces [i];
473 node = document.CreateElement ("namespace", null);
475 AddAttribute (node, "name", xns.Name);
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);
484 xns.AddCountersAttributes (node);
486 counters.PresentTotal++;
487 counters.AddPartialToTotal (xns.Counters);
491 count = other.Length;
492 for (int i = 0; i < count; i++) {
493 XMLNamespace n = other [i];
497 node = document.CreateElement ("namespace", null);
499 AddAttribute (node, "name", n.Name);
501 counters.ExtraTotal++;
505 XmlNode [] nodes = (XmlNode []) newNS.ToArray (typeof (XmlNode));
506 Array.Sort (nodes, XmlNodeComparer.Default);
507 foreach (XmlNode nn in nodes)
508 group.AppendChild (nn);
511 static Hashtable CreateHash (XMLNamespace [] other)
513 Hashtable result = new Hashtable ();
516 foreach (XMLNamespace n in other) {
517 result [n.Name] = i++;
524 public XmlDocument CompareAndGetDocument (XMLAssembly other)
526 XmlDocument doc = new XmlDocument ();
528 XmlNode parent = doc.CreateElement ("assemblies", null);
529 doc.AppendChild (parent);
531 CompareTo (doc, parent, other);
533 XmlNode decl = doc.CreateXmlDeclaration ("1.0", null, null);
534 doc.InsertBefore (decl, doc.DocumentElement);
540 class XMLNamespace : XMLData
545 public override void LoadData (XmlNode node)
548 throw new ArgumentNullException ("node");
550 if (node.Name != "namespace")
551 throw new FormatException ("Expecting <namespace>");
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"]);
560 if (classes.Name != "classes")
561 throw new FormatException ("Expecting <classes>. Got <" + classes.Name + ">");
563 types = (XMLClass []) LoadRecursive (classes.ChildNodes, typeof (XMLClass));
566 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
569 XMLNamespace nspace = (XMLNamespace) other;
571 XmlNode childA = doc.CreateElement ("classes", null);
572 parent.AppendChild (childA);
574 CompareTypes (childA, nspace != null ? nspace.types : new XMLClass [0]);
577 void CompareTypes (XmlNode parent, XMLClass [] other)
579 ArrayList newNodes = new ArrayList ();
580 Hashtable oh = CreateHash (other);
582 int count = (types == null) ? 0 : types.Length;
583 for (int i = 0; i < count; i++) {
584 XMLClass xclass = types [i];
586 node = document.CreateElement ("class", null);
588 AddAttribute (node, "name", xclass.Name);
589 AddAttribute (node, "type", xclass.Type);
592 if (oh.ContainsKey (xclass.Name))
593 idx = (int) oh [xclass.Name];
594 xclass.CompareTo (document, node, idx >= 0 ? other [idx] : new XMLClass ());
597 counters.AddPartialToPartial (xclass.Counters);
601 count = other.Length;
602 for (int i = 0; i < count; i++) {
603 XMLClass c = other [i];
604 if (c == null || IsMonoTODOAttribute (c.Name))
607 node = document.CreateElement ("class", null);
609 AddAttribute (node, "name", c.Name);
610 AddAttribute (node, "type", c.Type);
613 counters.ExtraTotal++;
617 XmlNode [] nodes = (XmlNode []) newNodes.ToArray (typeof (XmlNode));
618 Array.Sort (nodes, XmlNodeComparer.Default);
619 foreach (XmlNode nn in nodes)
620 parent.AppendChild (nn);
623 static Hashtable CreateHash (XMLClass [] other)
625 Hashtable result = new Hashtable ();
628 foreach (XMLClass c in other) {
629 result [c.Name] = i++;
641 class XMLClass : XMLData
651 XMLAttributes attributes;
652 XMLInterfaces interfaces;
653 XMLGenericTypeConstraints genericConstraints;
655 XMLConstructors constructors;
656 XMLProperties properties;
661 public override void LoadData (XmlNode node)
664 throw new ArgumentNullException ("node");
666 name = node.Attributes ["name"].Value;
667 type = node.Attributes ["type"].Value;
668 XmlAttribute xatt = node.Attributes ["base"];
670 baseName = xatt.Value;
672 xatt = node.Attributes ["sealed"];
673 isSealed = (xatt != null && xatt.Value == "true");
675 xatt = node.Attributes ["abstract"];
676 isAbstract = (xatt != null && xatt.Value == "true");
678 xatt = node.Attributes["serializable"];
679 isSerializable = (xatt != null && xatt.Value == "true");
681 xatt = node.Attributes["charset"];
683 charSet = xatt.Value;
685 xatt = node.Attributes["layout"];
689 XmlNode child = node.FirstChild;
691 // Console.Error.WriteLine ("Empty class {0} {1}", name, type);
695 if (child.Name == "attributes") {
696 attributes = new XMLAttributes ();
697 attributes.LoadData (child);
698 child = child.NextSibling;
701 if (child != null && child.Name == "interfaces") {
702 interfaces = new XMLInterfaces ();
703 interfaces.LoadData (child);
704 child = child.NextSibling;
707 if (child != null && child.Name == "generic-type-constraints") {
708 genericConstraints = new XMLGenericTypeConstraints ();
709 genericConstraints.LoadData (child);
710 child = child.NextSibling;
713 if (child != null && child.Name == "fields") {
714 fields = new XMLFields ();
715 fields.LoadData (child);
716 child = child.NextSibling;
719 if (child != null && child.Name == "constructors") {
720 constructors = new XMLConstructors ();
721 constructors.LoadData (child);
722 child = child.NextSibling;
725 if (child != null && child.Name == "properties") {
726 properties = new XMLProperties ();
727 properties.LoadData (child);
728 child = child.NextSibling;
731 if (child != null && child.Name == "events") {
732 events = new XMLEvents ();
733 events.LoadData (child);
734 child = child.NextSibling;
737 if (child != null && child.Name == "methods") {
738 methods = new XMLMethods ();
739 methods.LoadData (child);
740 child = child.NextSibling;
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
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 + ">");
757 nested = (XMLClass []) LoadRecursive (child.ChildNodes, typeof (XMLClass));
760 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
763 XMLClass oclass = (XMLClass) other;
765 if (attributes != null || oclass.attributes != null) {
766 if (attributes == null)
767 attributes = new XMLAttributes ();
769 attributes.CompareTo (doc, parent, oclass.attributes);
770 counters.AddPartialToPartial (attributes.Counters);
771 if (oclass.attributes != null && oclass.attributes.IsTodo) {
773 counters.TodoTotal++;
774 counters.ErrorTotal++;
775 AddAttribute (parent, "error", "todo");
776 if (oclass.attributes.Comment != null)
777 AddAttribute (parent, "comment", oclass.attributes.Comment);
781 if (type != oclass.type)
782 AddWarning (parent, "Class type is wrong: {0} != {1}", type, oclass.type);
784 if (baseName != oclass.baseName)
785 AddWarning (parent, "Base class is wrong: {0} != {1}", baseName, oclass.baseName);
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 ");
796 if (isSerializable != oclass.isSerializable)
797 AddWarning (parent, "Should {0}be serializable", isSerializable ? "" : "not ");
799 if (charSet != oclass.charSet)
800 AddWarning (parent, "CharSet is wrong: {0} != {1}", charSet, oclass.charSet);
802 if (layout != oclass.layout)
803 AddWarning (parent, "Layout is wrong: {0} != {1}", layout, oclass.layout);
805 if (interfaces != null || oclass.interfaces != null) {
806 if (interfaces == null)
807 interfaces = new XMLInterfaces ();
809 interfaces.CompareTo (doc, parent, oclass.interfaces);
810 counters.AddPartialToPartial (interfaces.Counters);
813 if (genericConstraints != null || oclass.genericConstraints != null) {
814 if (genericConstraints == null)
815 genericConstraints = new XMLGenericTypeConstraints ();
817 genericConstraints.CompareTo (doc, parent, oclass.genericConstraints);
818 counters.AddPartialToPartial (genericConstraints.Counters);
821 if (fields != null || oclass.fields != null) {
823 fields = new XMLFields ();
825 fields.CompareTo (doc, parent, oclass.fields);
826 counters.AddPartialToPartial (fields.Counters);
829 if (constructors != null || oclass.constructors != null) {
830 if (constructors == null)
831 constructors = new XMLConstructors ();
833 constructors.CompareTo (doc, parent, oclass.constructors);
834 counters.AddPartialToPartial (constructors.Counters);
837 if (properties != null || oclass.properties != null) {
838 if (properties == null)
839 properties = new XMLProperties ();
841 properties.CompareTo (doc, parent, oclass.properties);
842 counters.AddPartialToPartial (properties.Counters);
845 if (events != null || oclass.events != null) {
847 events = new XMLEvents ();
849 events.CompareTo (doc, parent, oclass.events);
850 counters.AddPartialToPartial (events.Counters);
853 if (methods != null || oclass.methods != null) {
855 methods = new XMLMethods ();
857 methods.CompareTo (doc, parent, oclass.methods);
858 counters.AddPartialToPartial (methods.Counters);
861 if (nested != null || oclass.nested != null) {
862 XmlNode n = doc.CreateElement ("classes", null);
863 parent.AppendChild (n);
864 CompareTypes (n, oclass.nested);
867 AddCountersAttributes (parent);
870 void CompareTypes (XmlNode parent, XMLClass [] other)
872 ArrayList newNodes = new ArrayList ();
873 Hashtable oh = CreateHash (other);
875 int count = (nested == null) ? 0 : nested.Length;
876 for (int i = 0; i < count; i++) {
877 XMLClass xclass = nested [i];
879 node = document.CreateElement ("class", null);
881 AddAttribute (node, "name", xclass.Name);
882 AddAttribute (node, "type", xclass.Type);
884 if (oh.ContainsKey (xclass.Name)) {
885 int idx = (int) oh [xclass.Name];
886 xclass.CompareTo (document, node, other [idx]);
888 counters.AddPartialToPartial (xclass.Counters);
890 // TODO: Should I count here?
891 AddAttribute (node, "presence", "missing");
893 counters.MissingTotal++;
898 count = other.Length;
899 for (int i = 0; i < count; i++) {
900 XMLClass c = other [i];
901 if (c == null || IsMonoTODOAttribute (c.Name))
904 node = document.CreateElement ("class", null);
906 AddAttribute (node, "name", c.Name);
907 AddAttribute (node, "type", c.Type);
910 counters.ExtraTotal++;
914 XmlNode [] nodes = (XmlNode []) newNodes.ToArray (typeof (XmlNode));
915 Array.Sort (nodes, XmlNodeComparer.Default);
916 foreach (XmlNode nn in nodes)
917 parent.AppendChild (nn);
920 static Hashtable CreateHash (XMLClass [] other)
922 Hashtable result = new Hashtable ();
925 foreach (XMLClass c in other) {
926 result [c.Name] = i++;
942 class XMLParameter : XMLData
951 XMLAttributes attributes;
953 public override void LoadData (XmlNode node)
956 throw new ArgumentNullException ("node");
958 if (node.Name != "parameter")
959 throw new ArgumentException ("Expecting <parameter>");
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;
973 XmlNode child = node.FirstChild;
977 if (child.Name == "attributes") {
978 attributes = new XMLAttributes ();
979 attributes.LoadData (child);
980 child = child.NextSibling;
984 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
988 XMLParameter oparm = (XMLParameter) other;
990 if (name != oparm.name)
991 AddWarning (parent, "Parameter name is wrong: {0} != {1}", name, oparm.name);
993 if (type != oparm.type)
994 AddWarning (parent, "Parameter type is wrong: {0} != {1}", type, oparm.type);
996 if (attrib != oparm.attrib)
997 AddWarning (parent, "Parameter attributes wrong: {0} != {1}", attrib, oparm.attrib);
999 if (direction != oparm.direction)
1000 AddWarning (parent, "Parameter direction wrong: {0} != {1}", direction, oparm.direction);
1002 if (isUnsafe != oparm.isUnsafe)
1003 AddWarning (parent, "Parameter unsafe wrong: {0} != {1}", isUnsafe, oparm.isUnsafe);
1005 if (isOptional != oparm.isOptional)
1006 AddWarning (parent, "Parameter optional wrong: {0} != {1}", isOptional, oparm.isOptional);
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);
1011 if (attributes != null || oparm.attributes != null) {
1012 if (attributes == null)
1013 attributes = new XMLAttributes ();
1015 attributes.CompareTo (doc, parent, oparm.attributes);
1016 counters.AddPartialToPartial (attributes.Counters);
1017 if (oparm.attributes != null && oparm.attributes.IsTodo) {
1019 counters.TodoTotal++;
1020 counters.ErrorTotal++;
1021 AddAttribute (parent, "error", "todo");
1022 if (oparm.attributes.Comment != null)
1023 AddAttribute (parent, "comment", oparm.attributes.Comment);
1028 public string Name {
1029 get { return name; }
1033 class XMLAttributeProperties: XMLNameGroup
1035 static Hashtable ignored_properties;
1036 static XMLAttributeProperties ()
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");
1047 ignored_properties.Add ("System.ObsoleteAttribute", "Message");
1048 ignored_properties.Add ("System.IO.IODescriptionAttribute", "Description");
1049 ignored_properties.Add ("System.Diagnostics.MonitoringDescriptionAttribute", "Description");
1052 Hashtable properties = new Hashtable ();
1055 public XMLAttributeProperties (string attribute)
1057 this.attribute = attribute;
1060 public override void LoadData(XmlNode node)
1063 throw new ArgumentNullException ("node");
1065 if (node.ChildNodes == null)
1068 string ignored = ignored_properties [attribute] as string;
1070 foreach (XmlNode n in node.ChildNodes) {
1071 string name = n.Attributes ["name"].Value;
1072 if (ignored == name)
1075 if (n.Attributes ["null"] != null) {
1076 properties.Add (name, null);
1079 string value = n.Attributes ["value"].Value;
1080 properties.Add (name, value);
1084 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
1086 this.document = doc;
1088 Hashtable other_properties = ((XMLAttributeProperties)other).properties;
1089 foreach (DictionaryEntry de in other_properties) {
1090 object other_value = properties [de.Key];
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);
1098 if (de.Value.Equals (other_value))
1101 AddWarning (parent, "Property '{0}' is '{1}' and should be '{2}'",
1102 de.Key, de.Value, other_value == null ? "null" : other_value);
1106 public override string GroupName {
1108 return "properties";
1112 public override string Name {
1119 class XMLAttributes : XMLNameGroup
1121 Hashtable properties = new Hashtable ();
1126 protected override bool CheckIfAdd (string value, XmlNode node)
1128 if (IsMonoTODOAttribute (value)) {
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;
1138 return !IsMeaninglessAttribute (value);
1141 protected override void CompareToInner (string name, XmlNode node, XMLNameGroup other)
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)
1148 this_prop.CompareTo (document, node, other_prop);
1149 counters.AddPartialToPartial (this_prop.Counters);
1152 public override string GetNodeKey (string name, XmlNode node)
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
1161 XmlNodeList attributes = node.ParentNode.SelectNodes("attribute[@name='" + name + "']");
1162 if (attributes.Count > 1) {
1163 ArrayList keyParts = new ArrayList ();
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);
1171 keyParts.Add (attrs["name"].Value + "=");
1175 // sort properties by name, as order of properties in XML is
1179 // insert name (type) of attribute
1180 keyParts.Insert (0, name);
1182 StringBuilder sb = new StringBuilder ();
1183 foreach (string value in keyParts) {
1187 key = sb.ToString ();
1195 protected override void LoadExtraData(string name, XmlNode node)
1197 XmlNode pNode = node.SelectSingleNode ("properties");
1199 if (IsMonoTODOAttribute (name)) {
1201 if (pNode.ChildNodes [0].Attributes ["value"] != null) {
1202 comment = pNode.ChildNodes [0].Attributes ["value"].Value;
1207 if (pNode != null) {
1208 XMLAttributeProperties p = new XMLAttributeProperties (name);
1211 properties[name] = p;
1215 public override string GroupName {
1216 get { return "attributes"; }
1219 public override string Name {
1220 get { return "attribute"; }
1223 public bool IsTodo {
1224 get { return isTodo; }
1227 public string Comment {
1228 get { return comment; }
1232 class XMLInterfaces : XMLNameGroup
1234 public override string GroupName {
1235 get { return "interfaces"; }
1238 public override string Name {
1239 get { return "interface"; }
1243 abstract class XMLGenericGroup : XMLNameGroup
1247 protected override void LoadExtraData (string name, XmlNode node)
1249 attributes = ((XmlElement) node).GetAttribute ("generic-attribute");
1252 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1254 base.CompareToInner (name, parent, other);
1256 XMLGenericGroup g = (XMLGenericGroup) other;
1257 if (attributes != g.attributes)
1258 AddWarning (parent, "Incorrect generic attributes: '{0}' != '{1}'", attributes, g.attributes);
1262 class XMLGenericTypeConstraints : XMLGenericGroup
1264 public override string GroupName {
1265 get { return "generic-type-constraints"; }
1268 public override string Name {
1269 get { return "generic-type-constraint"; }
1273 class XMLGenericMethodConstraints : XMLGenericGroup
1275 public override string GroupName {
1276 get { return "generic-method-constraints"; }
1279 public override string Name {
1280 get { return "generic-method-constraint"; }
1284 abstract class XMLMember : XMLNameGroup
1286 Hashtable attributeMap;
1287 Hashtable access = new Hashtable ();
1289 protected override void LoadExtraData (string name, XmlNode node)
1291 XmlAttribute xatt = node.Attributes ["attrib"];
1293 access [name] = xatt.Value;
1295 XmlNode orig = node;
1297 node = node.FirstChild;
1298 while (node != null) {
1299 if (node != null && node.Name == "attributes") {
1300 XMLAttributes a = new XMLAttributes ();
1302 if (attributeMap == null)
1303 attributeMap = new Hashtable ();
1305 attributeMap [name] = a;
1308 node = node.NextSibling;
1311 base.LoadExtraData (name, orig);
1314 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
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;
1323 if (mb != null && mb.attributeMap != null)
1324 oatt = mb.attributeMap [name] as XMLAttributes;
1326 if (att != null || oatt != null) {
1328 att = new XMLAttributes ();
1330 att.CompareTo (document, parent, oatt);
1331 counters.AddPartialToPartial(att.Counters);
1332 if (oatt != null && oatt.IsTodo) {
1334 counters.ErrorTotal++;
1335 AddAttribute (parent, "error", "todo");
1336 if (oatt.Comment != null)
1337 AddAttribute (parent, "comment", oatt.Comment);
1341 XMLMember member = (XMLMember) other;
1342 string acc = access [name] as string;
1347 if (member.access != null)
1348 oacc = member.access [name] as string;
1350 string accName = ConvertToString (Int32.Parse (acc));
1351 string oaccName = "";
1353 oaccName = ConvertToString (Int32.Parse (oacc));
1355 if (accName != oaccName)
1356 AddWarning (parent, "Incorrect attributes: '{0}' != '{1}'", accName, oaccName);
1359 protected virtual string ConvertToString (int att)
1365 class XMLFields : XMLMember
1367 Hashtable fieldTypes;
1368 Hashtable fieldValues;
1370 protected override void LoadExtraData (string name, XmlNode node)
1372 XmlAttribute xatt = node.Attributes ["fieldtype"];
1374 if (fieldTypes == null)
1375 fieldTypes = new Hashtable ();
1377 fieldTypes [name] = xatt.Value;
1380 xatt = node.Attributes ["value"];
1382 if (fieldValues == null)
1383 fieldValues = new Hashtable ();
1385 fieldValues[name] = xatt.Value;
1388 base.LoadExtraData (name, node);
1391 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
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;
1401 if (ftype != oftype)
1402 AddWarning (parent, "Field type is {0} and should be {1}", oftype, ftype);
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;
1410 if (fvalue != ofvalue)
1411 AddWarning (parent, "Field value is {0} and should be {1}", ofvalue, fvalue);
1415 protected override string ConvertToString (int att)
1417 FieldAttributes fa = (FieldAttributes) att;
1418 return fa.ToString ();
1421 public override string GroupName {
1422 get { return "fields"; }
1425 public override string Name {
1426 get { return "field"; }
1430 class XMLParameters : XMLNameGroup
1432 public override void LoadData (XmlNode node)
1435 throw new ArgumentNullException ("node");
1437 if (node.Name != GroupName)
1438 throw new FormatException (String.Format ("Expecting <{0}>", GroupName));
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 ();
1446 keys.Add (key, parm);
1447 LoadExtraData (key, n);
1451 public override string GroupName {
1453 return "parameters";
1457 public override string Name {
1463 public override string GetNodeKey (string name, XmlNode node)
1465 return node.Attributes["position"].Value;
1468 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
1470 this.document = doc;
1472 group = doc.CreateElement (GroupName, null);
1474 Hashtable okeys = null;
1475 if (other != null && ((XMLParameters) other).keys != null) {
1476 okeys = ((XMLParameters) other).keys;
1479 XmlNode node = null;
1480 bool onull = (okeys == 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);
1489 if (!onull && HasKey (key, okeys)) {
1490 parm.CompareTo (document, node, okeys[key]);
1491 counters.AddPartialToPartial (parm.Counters);
1495 AddAttribute (node, "presence", "missing");
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);
1511 if (group.HasChildNodes)
1512 parent.AppendChild (group);
1516 class XMLProperties : XMLMember
1518 Hashtable nameToMethod = new Hashtable ();
1520 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1522 Counters copy = counters;
1523 counters = new Counters();
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) {
1531 m = new XMLMethods ();
1533 m.CompareTo(document, parent, om);
1534 counters.AddPartialToPartial(m.Counters);
1538 base.CompareToInner (name, parent, other);
1539 AddCountersAttributes(parent);
1541 copy.AddPartialToPartial(counters);
1545 protected override void LoadExtraData (string name, XmlNode node)
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);
1555 nameToMethod [key] = m;
1558 node = node.NextSibling;
1561 base.LoadExtraData (name, orig);
1564 public override string GetNodeKey (string name, XmlNode node)
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 : "")
1574 public override string GroupName {
1575 get { return "properties"; }
1578 public override string Name {
1579 get { return "property"; }
1583 class XMLEvents : XMLMember
1585 Hashtable eventTypes;
1586 Hashtable nameToMethod = new Hashtable ();
1588 protected override void LoadExtraData (string name, XmlNode node)
1590 XmlAttribute xatt = node.Attributes ["eventtype"];
1592 if (eventTypes == null)
1593 eventTypes = new Hashtable ();
1595 eventTypes [name] = xatt.Value;
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);
1605 nameToMethod [key] = m;
1608 child = child.NextSibling;
1611 base.LoadExtraData (name, node);
1614 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1616 Counters copy = counters;
1617 counters = new Counters ();
1620 base.CompareToInner (name, parent, other);
1621 AddCountersAttributes (parent);
1622 if (eventTypes == null)
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;
1631 if (etype != oetype)
1632 AddWarning (parent, "Event type is {0} and should be {1}", oetype, etype);
1634 XMLMethods m = nameToMethod [name] as XMLMethods;
1635 XMLMethods om = evt.nameToMethod [name] as XMLMethods;
1636 if (m != null || om != null) {
1638 m = new XMLMethods ();
1640 m.CompareTo (document, parent, om);
1641 counters.AddPartialToPartial (m.Counters);
1644 AddCountersAttributes (parent);
1645 copy.AddPartialToPartial (counters);
1650 protected override string ConvertToString (int att)
1652 EventAttributes ea = (EventAttributes) att;
1653 return ea.ToString ();
1656 public override string GroupName {
1657 get { return "events"; }
1660 public override string Name {
1661 get { return "event"; }
1665 class XMLMethods : XMLMember
1667 Hashtable returnTypes;
1668 Hashtable parameters;
1669 Hashtable genericConstraints;
1670 Hashtable signatureFlags;
1682 protected override void LoadExtraData (string name, XmlNode node)
1684 XmlAttribute xatt = node.Attributes ["returntype"];
1686 if (returnTypes == null)
1687 returnTypes = new Hashtable ();
1689 returnTypes [name] = xatt.Value;
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;
1707 XmlNode parametersNode = node.SelectSingleNode ("parameters");
1708 if (parametersNode != null) {
1709 if (parameters == null)
1710 parameters = new Hashtable ();
1712 XMLParameters parms = new XMLParameters ();
1713 parms.LoadData (parametersNode);
1715 parameters[name] = parms;
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;
1727 base.LoadExtraData (name, node);
1730 public override string GetNodeKey (string name, XmlNode node)
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
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;
1747 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1749 // create backup of actual counters
1750 Counters copy = counters;
1751 // initialize counters for current method
1752 counters = new Counters();
1755 base.CompareToInner(name, parent, other);
1756 XMLMethods methods = (XMLMethods) other;
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;
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));
1773 AddWarning (parent, String.Format ("{0} and should be {1}", oflags, flags));
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;
1782 if (rtype != ortype)
1783 AddWarning (parent, "Return type is {0} and should be {1}", ortype, rtype);
1786 if (parameters != null) {
1787 XMLParameters parms = parameters[name] as XMLParameters;
1788 parms.CompareTo (document, parent, methods.parameters[name]);
1789 counters.AddPartialToPartial (parms.Counters);
1792 // output counter attributes in result document
1793 AddCountersAttributes(parent);
1795 // add temporary counters to actual counters
1796 copy.AddPartialToPartial(counters);
1797 // restore backup of actual counters
1802 protected override string ConvertToString (int att)
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;
1811 // ignore the HasSecurity attribute for now
1812 if ((ma & MethodAttributes.HasSecurity) != 0)
1813 ma = (MethodAttributes) (att - (int) MethodAttributes.HasSecurity);
1815 // ignore the RequireSecObject attribute for now
1816 if ((ma & MethodAttributes.RequireSecObject) != 0)
1817 ma = (MethodAttributes) (att - (int) MethodAttributes.RequireSecObject);
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);
1823 return ma.ToString ();
1826 public override string GroupName {
1827 get { return "methods"; }
1830 public override string Name {
1831 get { return "method"; }
1835 class XMLConstructors : XMLMethods
1837 public override string GroupName {
1838 get { return "constructors"; }
1841 public override string Name {
1842 get { return "constructor"; }
1846 class XmlNodeComparer : IComparer
1848 public static XmlNodeComparer Default = new XmlNodeComparer ();
1850 public int Compare (object a, object b)
1852 XmlNode na = (XmlNode) a;
1853 XmlNode nb = (XmlNode) b;
1854 return String.Compare (na.Attributes ["name"].Value, nb.Attributes ["name"].Value);