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)
8 // (C) 2003 Novell, Inc (http://www.novell.com)
12 using System.Collections;
18 namespace Mono.AssemblyCompare
22 public static int Main (string [] args)
27 XMLAssembly ms = CreateXMLAssembly (args [0]);
28 XMLAssembly mono = CreateXMLAssembly (args [1]);
29 XmlDocument doc = ms.CompareAndGetDocument (mono);
31 var writer = new WellFormedXmlWriter (new XmlTextWriter (Console.Out) { Formatting = Formatting.Indented });
37 static XMLAssembly CreateXMLAssembly (string file)
39 XmlDocument doc = new XmlDocument ();
40 doc.Load (File.OpenRead (file));
42 XmlNode node = doc.SelectSingleNode ("/assemblies/assembly");
43 XMLAssembly result = new XMLAssembly ();
45 result.LoadData (node);
46 } catch (Exception e) {
47 Console.Error.WriteLine ("Error loading {0}: {1}\n{2}", file, e.Message, e);
58 public int PresentTotal;
60 public int MissingTotal;
65 public int ExtraTotal;
67 public int WarningTotal;
68 public int ErrorTotal;
74 public void AddPartialToPartial (Counters other)
76 Present += other.Present;
78 Missing += other.Missing;
81 Warning += other.Warning;
82 AddPartialToTotal (other);
85 public void AddPartialToTotal (Counters other)
87 PresentTotal += other.Present;
88 ExtraTotal += other.Extra;
89 MissingTotal += other.Missing;
91 TodoTotal += other.Todo;
92 WarningTotal += other.Warning;
95 public void AddTotalToPartial (Counters other)
97 Present += other.PresentTotal;
98 Extra += other.ExtraTotal;
99 Missing += other.MissingTotal;
101 Todo += other.TodoTotal;
102 Warning += other.WarningTotal;
103 AddTotalToTotal (other);
106 public void AddTotalToTotal (Counters other)
108 PresentTotal += other.PresentTotal;
109 ExtraTotal += other.ExtraTotal;
110 MissingTotal += other.MissingTotal;
112 TodoTotal += other.TodoTotal;
113 WarningTotal += other.WarningTotal;
114 ErrorTotal += other.ErrorTotal;
118 get { return Present + Missing; }
121 public int AbsTotal {
122 get { return PresentTotal + MissingTotal; }
126 get { return Present - Todo; }
130 get { return PresentTotal - TodoTotal - ErrorTotal; }
133 public override string ToString ()
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);
148 return sw.GetStringBuilder ().ToString ();
152 abstract class XMLData
154 protected XmlDocument document;
155 protected Counters counters;
160 counters = new Counters ();
163 public virtual void LoadData (XmlNode node)
167 protected object [] LoadRecursive (XmlNodeList nodeList, Type type)
169 ArrayList list = new ArrayList ();
170 foreach (XmlNode node in nodeList) {
171 XMLData data = (XMLData) Activator.CreateInstance (type);
172 data.LoadData (node);
176 return (object []) list.ToArray (type);
179 protected void AddAttribute (XmlNode node, string name, string value)
181 XmlAttribute attr = document.CreateAttribute (name);
183 node.Attributes.Append (attr);
186 protected void AddExtra (XmlNode node)
188 //TODO: count all the subnodes?
189 AddAttribute (node, "presence", "extra");
190 AddAttribute (node, "ok", "1");
191 AddAttribute (node, "ok_total", "1");
192 AddAttribute (node, "extra", "1");
193 AddAttribute (node, "extra_total", "1");
196 public void AddCountersAttributes (XmlNode node)
198 if (counters.Missing > 0)
199 AddAttribute (node, "missing", counters.Missing.ToString ());
201 if (counters.Present > 0)
202 AddAttribute (node, "present", counters.Present.ToString ());
204 if (counters.Extra > 0)
205 AddAttribute (node, "extra", counters.Extra.ToString ());
208 AddAttribute (node, "ok", counters.Ok.ToString ());
210 if (counters.Total > 0) {
211 int percent = (100 * counters.Ok / counters.Total);
212 AddAttribute (node, "complete", percent.ToString ());
215 if (counters.Todo > 0)
216 AddAttribute (node, "todo", counters.Todo.ToString ());
218 if (counters.Warning > 0)
219 AddAttribute (node, "warning", counters.Warning.ToString ());
221 if (counters.MissingTotal > 0)
222 AddAttribute (node, "missing_total", counters.MissingTotal.ToString ());
224 if (counters.PresentTotal > 0)
225 AddAttribute (node, "present_total", counters.PresentTotal.ToString ());
227 if (counters.ExtraTotal > 0)
228 AddAttribute (node, "extra_total", counters.ExtraTotal.ToString ());
230 if (counters.OkTotal > 0)
231 AddAttribute (node, "ok_total", counters.OkTotal.ToString ());
233 if (counters.AbsTotal > 0) {
234 int percent = (100 * counters.OkTotal / counters.AbsTotal);
235 AddAttribute (node, "complete_total", percent.ToString ());
238 if (counters.TodoTotal > 0) {
239 AddAttribute (node, "todo_total", counters.TodoTotal.ToString ());
240 //TODO: should be different on error. check error cases in corcompare.
241 AddAttribute (node, "error_total", counters.Todo.ToString ());
244 if (counters.WarningTotal > 0)
245 AddAttribute (node, "warning_total", counters.WarningTotal.ToString ());
249 protected void AddWarning (XmlNode parent, string fmt, params object [] args)
253 XmlNode warnings = parent.SelectSingleNode ("warnings");
254 if (warnings == null) {
255 warnings = document.CreateElement ("warnings", null);
256 parent.AppendChild (warnings);
259 AddAttribute (parent, "error", "warning");
260 XmlNode warning = document.CreateElement ("warning", null);
261 AddAttribute (warning, "text", String.Format (fmt, args));
262 warnings.AppendChild (warning);
265 public bool HaveWarnings {
266 get { return haveWarnings; }
269 public Counters Counters {
270 get { return counters; }
273 public abstract void CompareTo (XmlDocument doc, XmlNode parent, object other);
276 abstract class XMLNameGroup : XMLData
278 protected XmlNode group;
279 protected Hashtable keys;
281 public override void LoadData (XmlNode node)
284 throw new ArgumentNullException ("node");
286 if (node.Name != GroupName)
287 throw new FormatException (String.Format ("Expecting <{0}>", GroupName));
289 keys = new Hashtable ();
290 foreach (XmlNode n in node.ChildNodes) {
291 string name = n.Attributes ["name"].Value;
292 if (CheckIfAdd (name, n)) {
293 string key = GetNodeKey (name, n);
294 //keys.Add (key, name);
296 LoadExtraData (key, n);
301 protected virtual bool CheckIfAdd (string value, XmlNode node)
306 protected virtual void LoadExtraData (string name, XmlNode node)
310 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
314 group = doc.CreateElement (GroupName, null);
316 Hashtable okeys = null;
317 if (other != null && ((XMLNameGroup) other).keys != null) {
318 okeys = ((XMLNameGroup) other).keys;
322 bool onull = (okeys == null);
324 foreach (DictionaryEntry entry in keys) {
325 node = doc.CreateElement (Name, null);
326 group.AppendChild (node);
327 string key = (string) entry.Key;
328 string name = (string) entry.Value;
329 AddAttribute (node, "name", name);
331 if (!onull && HasKey (key, okeys)) {
332 CompareToInner (key, node, (XMLNameGroup) other);
336 AddAttribute (node, "presence", "missing");
342 if (!onull && okeys.Count != 0) {
343 foreach (string value in okeys.Values) {
344 node = doc.CreateElement (Name, null);
345 AddAttribute (node, "name", (string) value);
346 AddAttribute (node, "presence", "extra");
347 group.AppendChild (node);
352 if (group.HasChildNodes)
353 parent.AppendChild (group);
356 protected virtual void CompareToInner (string name, XmlNode node, XMLNameGroup other)
360 public virtual string GetNodeKey (string name, XmlNode node)
365 public virtual bool HasKey (string key, Hashtable other)
367 return other.ContainsKey (key);
370 public abstract string GroupName { get; }
371 public abstract string Name { get; }
374 class XMLAssembly : XMLData
376 XMLAttributes attributes;
377 XMLNamespace [] namespaces;
381 public override void LoadData (XmlNode node)
384 throw new ArgumentNullException ("node");
386 name = node.Attributes ["name"].Value;
387 version = node.Attributes ["version"].Value;
388 XmlNode atts = node.FirstChild;
389 attributes = new XMLAttributes ();
390 if (atts.Name == "attributes") {
391 attributes.LoadData (atts);
392 atts = atts.NextSibling;
395 if (atts == null || atts.Name != "namespaces") {
396 Console.Error.WriteLine ("Warning: no namespaces found!");
400 namespaces = (XMLNamespace []) LoadRecursive (atts.ChildNodes, typeof (XMLNamespace));
403 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
405 XMLAssembly assembly = (XMLAssembly) other;
407 XmlNode childA = doc.CreateElement ("assembly", null);
408 AddAttribute (childA, "name", name);
409 AddAttribute (childA, "version", version);
410 if (name != assembly.name)
411 AddWarning (childA, "Assembly names not equal: {0}, {1}", name, assembly.name);
413 if (version != assembly.version)
414 AddWarning (childA, "Assembly version not equal: {0}, {1}", version, assembly.version);
416 parent.AppendChild (childA);
418 attributes.CompareTo (doc, childA, assembly.attributes);
419 counters.AddPartialToPartial (attributes.Counters);
421 CompareNamespaces (childA, assembly.namespaces);
422 if (assembly.attributes != null && assembly.attributes.IsTodo) {
424 counters.TodoTotal++;
425 counters.ErrorTotal++;
426 AddAttribute (childA, "error", "todo");
427 if (assembly.attributes.Comment != null)
428 AddAttribute (childA, "comment", assembly.attributes.Comment);
431 AddCountersAttributes (childA);
434 void CompareNamespaces (XmlNode parent, XMLNamespace [] other)
436 ArrayList newNS = new ArrayList ();
437 XmlNode group = document.CreateElement ("namespaces", null);
438 parent.AppendChild (group);
440 Hashtable oh = CreateHash (other);
442 int count = (namespaces == null) ? 0 : namespaces.Length;
443 for (int i = 0; i < count; i++) {
444 XMLNamespace xns = namespaces [i];
446 node = document.CreateElement ("namespace", null);
448 AddAttribute (node, "name", xns.Name);
451 if (oh.ContainsKey (xns.Name))
452 idx = (int) oh [xns.Name];
453 XMLNamespace ons = idx >= 0 ? (XMLNamespace) other [idx] : null;
454 xns.CompareTo (document, node, ons);
457 xns.AddCountersAttributes (node);
459 counters.PresentTotal++;
460 counters.AddPartialToTotal (xns.Counters);
464 count = other.Length;
465 for (int i = 0; i < count; i++) {
466 XMLNamespace n = other [i];
470 node = document.CreateElement ("namespace", null);
472 AddAttribute (node, "name", n.Name);
474 counters.ExtraTotal++;
478 XmlNode [] nodes = (XmlNode []) newNS.ToArray (typeof (XmlNode));
479 Array.Sort (nodes, XmlNodeComparer.Default);
480 foreach (XmlNode nn in nodes)
481 group.AppendChild (nn);
484 static Hashtable CreateHash (XMLNamespace [] other)
486 Hashtable result = new Hashtable ();
489 foreach (XMLNamespace n in other) {
490 result [n.Name] = i++;
497 public XmlDocument CompareAndGetDocument (XMLAssembly other)
499 XmlDocument doc = new XmlDocument ();
501 XmlNode parent = doc.CreateElement ("assemblies", null);
502 doc.AppendChild (parent);
504 CompareTo (doc, parent, other);
506 XmlNode decl = doc.CreateXmlDeclaration ("1.0", null, null);
507 doc.InsertBefore (decl, doc.DocumentElement);
513 class XMLNamespace : XMLData
518 public override void LoadData (XmlNode node)
521 throw new ArgumentNullException ("node");
523 if (node.Name != "namespace")
524 throw new FormatException ("Expecting <namespace>");
526 name = node.Attributes ["name"].Value;
527 XmlNode classes = node.FirstChild;
528 if (classes == null) {
529 Console.Error.WriteLine ("Warning: no classes for {0}", node.Attributes ["name"]);
533 if (classes.Name != "classes")
534 throw new FormatException ("Expecting <classes>. Got <" + classes.Name + ">");
536 types = (XMLClass []) LoadRecursive (classes.ChildNodes, typeof (XMLClass));
539 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
542 XMLNamespace nspace = (XMLNamespace) other;
544 XmlNode childA = doc.CreateElement ("classes", null);
545 parent.AppendChild (childA);
547 CompareTypes (childA, nspace != null ? nspace.types : new XMLClass [0]);
550 void CompareTypes (XmlNode parent, XMLClass [] other)
552 ArrayList newNodes = new ArrayList ();
553 Hashtable oh = CreateHash (other);
555 int count = (types == null) ? 0 : types.Length;
556 for (int i = 0; i < count; i++) {
557 XMLClass xclass = types [i];
559 node = document.CreateElement ("class", null);
561 AddAttribute (node, "name", xclass.Name);
562 AddAttribute (node, "type", xclass.Type);
565 if (oh.ContainsKey (xclass.Name))
566 idx = (int) oh [xclass.Name];
567 xclass.CompareTo (document, node, idx >= 0 ? other [idx] : new XMLClass ());
570 counters.AddPartialToPartial (xclass.Counters);
574 count = other.Length;
575 for (int i = 0; i < count; i++) {
576 XMLClass c = other [i];
577 if (c == null || c.Name.EndsWith ("TODOAttribute"))
580 node = document.CreateElement ("class", null);
582 AddAttribute (node, "name", c.Name);
583 AddAttribute (node, "type", c.Type);
586 counters.ExtraTotal++;
590 XmlNode [] nodes = (XmlNode []) newNodes.ToArray (typeof (XmlNode));
591 Array.Sort (nodes, XmlNodeComparer.Default);
592 foreach (XmlNode nn in nodes)
593 parent.AppendChild (nn);
596 static Hashtable CreateHash (XMLClass [] other)
598 Hashtable result = new Hashtable ();
601 foreach (XMLClass c in other) {
602 result [c.Name] = i++;
614 class XMLClass : XMLData
624 XMLAttributes attributes;
625 XMLInterfaces interfaces;
626 XMLGenericParameters genericParameters;
628 XMLConstructors constructors;
629 XMLProperties properties;
634 public override void LoadData (XmlNode node)
637 throw new ArgumentNullException ("node");
639 name = node.Attributes ["name"].Value;
640 type = node.Attributes ["type"].Value;
641 XmlAttribute xatt = node.Attributes ["base"];
643 baseName = xatt.Value;
645 xatt = node.Attributes ["sealed"];
646 isSealed = (xatt != null && xatt.Value == "true");
648 xatt = node.Attributes ["abstract"];
649 isAbstract = (xatt != null && xatt.Value == "true");
651 xatt = node.Attributes["serializable"];
652 isSerializable = (xatt != null && xatt.Value == "true");
654 xatt = node.Attributes["charset"];
656 charSet = xatt.Value;
658 xatt = node.Attributes["layout"];
662 XmlNode child = node.FirstChild;
664 // Console.Error.WriteLine ("Empty class {0} {1}", name, type);
668 if (child.Name == "attributes") {
669 attributes = new XMLAttributes ();
670 attributes.LoadData (child);
671 child = child.NextSibling;
674 if (child != null && child.Name == "interfaces") {
675 interfaces = new XMLInterfaces ();
676 interfaces.LoadData (child);
677 child = child.NextSibling;
680 if (child != null && child.Name == "generic-parameters") {
681 genericParameters = new XMLGenericParameters ();
682 genericParameters.LoadData (child);
683 child = child.NextSibling;
686 if (child != null && child.Name == "fields") {
687 fields = new XMLFields ();
688 fields.LoadData (child);
689 child = child.NextSibling;
692 if (child != null && child.Name == "constructors") {
693 constructors = new XMLConstructors ();
694 constructors.LoadData (child);
695 child = child.NextSibling;
698 if (child != null && child.Name == "properties") {
699 properties = new XMLProperties ();
700 properties.LoadData (child);
701 child = child.NextSibling;
704 if (child != null && child.Name == "events") {
705 events = new XMLEvents ();
706 events.LoadData (child);
707 child = child.NextSibling;
710 if (child != null && child.Name == "methods") {
711 methods = new XMLMethods ();
712 methods.LoadData (child);
713 child = child.NextSibling;
719 if (child.Name != "classes") {
720 Console.WriteLine ("name: {0} type: {1} {2}", name, type, child.NodeType);
721 throw new FormatException ("Expecting <classes>. Got <" + child.Name + ">");
724 nested = (XMLClass []) LoadRecursive (child.ChildNodes, typeof (XMLClass));
727 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
730 XMLClass oclass = (XMLClass) other;
732 if (attributes != null || oclass.attributes != null) {
733 if (attributes == null)
734 attributes = new XMLAttributes ();
736 attributes.CompareTo (doc, parent, oclass.attributes);
737 counters.AddPartialToPartial (attributes.Counters);
738 if (oclass.attributes != null && oclass.attributes.IsTodo) {
740 counters.TodoTotal++;
741 counters.ErrorTotal++;
742 AddAttribute (parent, "error", "todo");
743 if (oclass.attributes.Comment != null)
744 AddAttribute (parent, "comment", oclass.attributes.Comment);
748 if (type != oclass.type)
749 AddWarning (parent, "Class type is wrong: {0} != {1}", type, oclass.type);
751 if (baseName != oclass.baseName)
752 AddWarning (parent, "Base class is wrong: {0} != {1}", baseName, oclass.baseName);
754 if (isAbstract != oclass.isAbstract || isSealed != oclass.isSealed) {
755 if ((isAbstract && isSealed) || (oclass.isAbstract && oclass.isSealed))
756 AddWarning (parent, "Should {0}be static", (isAbstract && isSealed) ? "" : "not ");
757 else if (isAbstract != oclass.isAbstract)
758 AddWarning (parent, "Should {0}be abstract", isAbstract ? "" : "not ");
759 else if (isSealed != oclass.isSealed)
760 AddWarning (parent, "Should {0}be sealed", isSealed ? "" : "not ");
763 if (isSerializable != oclass.isSerializable)
764 AddWarning (parent, "Should {0}be serializable", isSerializable ? "" : "not ");
766 if (charSet != oclass.charSet)
767 AddWarning (parent, "CharSet is wrong: {0} != {1}", charSet, oclass.charSet);
769 if (layout != oclass.layout)
770 AddWarning (parent, "Layout is wrong: {0} != {1}", layout, oclass.layout);
772 if (interfaces != null || oclass.interfaces != null) {
773 if (interfaces == null)
774 interfaces = new XMLInterfaces ();
776 interfaces.CompareTo (doc, parent, oclass.interfaces);
777 counters.AddPartialToPartial (interfaces.Counters);
780 if (genericParameters != null || oclass.genericParameters != null) {
781 if (genericParameters == null)
782 genericParameters = new XMLGenericParameters ();
784 genericParameters.CompareTo (doc, parent, oclass.genericParameters);
785 counters.AddPartialToPartial (genericParameters.Counters);
788 if (fields != null || oclass.fields != null) {
790 fields = new XMLFields ();
792 fields.CompareTo (doc, parent, oclass.fields);
793 counters.AddPartialToPartial (fields.Counters);
796 if (constructors != null || oclass.constructors != null) {
797 if (constructors == null)
798 constructors = new XMLConstructors ();
800 constructors.CompareTo (doc, parent, oclass.constructors);
801 counters.AddPartialToPartial (constructors.Counters);
804 if (properties != null || oclass.properties != null) {
805 if (properties == null)
806 properties = new XMLProperties ();
808 properties.CompareTo (doc, parent, oclass.properties);
809 counters.AddPartialToPartial (properties.Counters);
812 if (events != null || oclass.events != null) {
814 events = new XMLEvents ();
816 events.CompareTo (doc, parent, oclass.events);
817 counters.AddPartialToPartial (events.Counters);
820 if (methods != null || oclass.methods != null) {
822 methods = new XMLMethods ();
824 methods.CompareTo (doc, parent, oclass.methods);
825 counters.AddPartialToPartial (methods.Counters);
828 if (nested != null || oclass.nested != null) {
829 XmlNode n = doc.CreateElement ("classes", null);
830 parent.AppendChild (n);
831 CompareTypes (n, oclass.nested);
834 AddCountersAttributes (parent);
837 void CompareTypes (XmlNode parent, XMLClass [] other)
839 ArrayList newNodes = new ArrayList ();
840 Hashtable oh = CreateHash (other);
842 int count = (nested == null) ? 0 : nested.Length;
843 for (int i = 0; i < count; i++) {
844 XMLClass xclass = nested [i];
846 node = document.CreateElement ("class", null);
848 AddAttribute (node, "name", xclass.Name);
849 AddAttribute (node, "type", xclass.Type);
851 if (oh.ContainsKey (xclass.Name)) {
852 int idx = (int) oh [xclass.Name];
853 xclass.CompareTo (document, node, other [idx]);
855 counters.AddPartialToPartial (xclass.Counters);
857 // TODO: Should I count here?
858 AddAttribute (node, "presence", "missing");
860 counters.MissingTotal++;
865 count = other.Length;
866 for (int i = 0; i < count; i++) {
867 XMLClass c = other [i];
868 if (c == null || c.Name.EndsWith ("TODOAttribute"))
871 node = document.CreateElement ("class", null);
873 AddAttribute (node, "name", c.Name);
876 counters.ExtraTotal++;
880 XmlNode [] nodes = (XmlNode []) newNodes.ToArray (typeof (XmlNode));
881 Array.Sort (nodes, XmlNodeComparer.Default);
882 foreach (XmlNode nn in nodes)
883 parent.AppendChild (nn);
886 static Hashtable CreateHash (XMLClass [] other)
888 Hashtable result = new Hashtable ();
891 foreach (XMLClass c in other) {
892 result [c.Name] = i++;
908 class XMLParameter : XMLData
918 public override void LoadData (XmlNode node)
921 throw new ArgumentNullException ("node");
923 if (node.Name != "parameter")
924 throw new ArgumentException ("Expecting <parameter>");
926 name = node.Attributes["name"].Value;
927 type = node.Attributes["type"].Value;
928 attrib = node.Attributes["attrib"].Value;
929 if (node.Attributes ["direction"] != null)
930 direction = node.Attributes["direction"].Value;
931 if (node.Attributes["unsafe"] != null)
932 isUnsafe = bool.Parse (node.Attributes["unsafe"].Value);
933 if (node.Attributes["optional"] != null)
934 isOptional = bool.Parse (node.Attributes["optional"].Value);
935 if (node.Attributes["defaultValue"] != null)
936 defaultValue = node.Attributes["defaultValue"].Value;
939 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
943 XMLParameter oparm = (XMLParameter) other;
945 if (type != oparm.type)
946 AddWarning (parent, "Parameter type is wrong: {0} != {1}", type, oparm.type);
948 if (attrib != oparm.attrib)
949 AddWarning (parent, "Parameter attributes wrong: {0} != {1}", attrib, oparm.attrib);
951 if (direction != oparm.direction)
952 AddWarning (parent, "Parameter direction wrong: {0} != {1}", direction, oparm.direction);
954 if (isUnsafe != oparm.isUnsafe)
955 AddWarning (parent, "Parameter unsafe wrong: {0} != {1}", isUnsafe, oparm.isUnsafe);
957 if (isOptional != oparm.isOptional)
958 AddWarning (parent, "Parameter optional wrong: {0} != {1}", isOptional, oparm.isOptional);
960 if (defaultValue != oparm.defaultValue)
961 AddWarning (parent, "Parameter default value wrong: {0} != {1}", (defaultValue == null) ? "(no default value)" : defaultValue, (oparm.defaultValue == null) ? "(no default value)" : oparm.defaultValue);
969 class XMLAttributeProperties: XMLNameGroup
971 static Hashtable ignored_properties;
972 static XMLAttributeProperties ()
974 ignored_properties = new Hashtable ();
975 ignored_properties.Add ("System.Reflection.AssemblyKeyFileAttribute", "KeyFile");
976 ignored_properties.Add ("System.Reflection.AssemblyCompanyAttribute", "Company");
977 ignored_properties.Add ("System.Reflection.AssemblyConfigurationAttribute", "Configuration");
978 ignored_properties.Add ("System.Reflection.AssemblyCopyrightAttribute", "Copyright");
979 ignored_properties.Add ("System.Reflection.AssemblyProductAttribute", "Product");
980 ignored_properties.Add ("System.Reflection.AssemblyTrademarkAttribute", "Trademark");
981 ignored_properties.Add ("System.Reflection.AssemblyInformationalVersionAttribute", "InformationalVersion");
983 ignored_properties.Add ("System.ObsoleteAttribute", "Message");
984 ignored_properties.Add ("System.IO.IODescriptionAttribute", "Description");
985 ignored_properties.Add ("System.Diagnostics.MonitoringDescriptionAttribute", "Description");
988 Hashtable properties = new Hashtable ();
991 public XMLAttributeProperties (string attribute)
993 this.attribute = attribute;
996 public override void LoadData(XmlNode node)
999 throw new ArgumentNullException ("node");
1001 if (node.ChildNodes == null)
1004 string ignored = ignored_properties [attribute] as string;
1006 foreach (XmlNode n in node.ChildNodes) {
1007 string name = n.Attributes ["name"].Value;
1008 if (ignored == name)
1011 if (n.Attributes ["null"] != null) {
1012 properties.Add (name, null);
1015 string value = n.Attributes ["value"].Value;
1016 properties.Add (name, value);
1020 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
1022 this.document = doc;
1024 Hashtable other_properties = ((XMLAttributeProperties)other).properties;
1025 foreach (DictionaryEntry de in other_properties) {
1026 object other_value = properties [de.Key];
1028 if (de.Value == null) {
1029 if (other_value != null)
1030 AddWarning (parent, "Property '{0}' is 'null' and should be '{1}'", de.Key, other_value);
1034 if (de.Value.Equals (other_value))
1037 AddWarning (parent, "Property '{0}' is '{1}' and should be '{2}'",
1038 de.Key, de.Value, other_value == null ? "null" : other_value);
1042 public override string GroupName {
1044 return "properties";
1048 public override string Name {
1055 class XMLAttributes : XMLNameGroup
1057 Hashtable properties = new Hashtable ();
1062 protected override bool CheckIfAdd (string value, XmlNode node)
1064 if (value.EndsWith ("TODOAttribute")) {
1067 XmlNode pNode = node.SelectSingleNode ("properties");
1068 if (pNode.ChildNodes [0].Attributes ["value"] != null) {
1069 comment = pNode.ChildNodes [0].Attributes ["value"].Value;
1077 protected override void CompareToInner (string name, XmlNode node, XMLNameGroup other)
1079 XMLAttributeProperties other_prop = ((XMLAttributes)other).properties [name] as XMLAttributeProperties;
1080 XMLAttributeProperties this_prop = properties [name] as XMLAttributeProperties;
1081 if (other_prop == null || this_prop == null)
1084 this_prop.CompareTo (document, node, other_prop);
1085 counters.AddPartialToPartial (this_prop.Counters);
1088 public override string GetNodeKey (string name, XmlNode node)
1092 // if multiple attributes with the same name (type) exist, then we
1093 // cannot be sure which attributes correspond, so we must use the
1094 // name of the attribute (type) and the name/value of its properties
1097 XmlNodeList attributes = node.ParentNode.SelectNodes("attribute[@name='" + name + "']");
1098 if (attributes.Count > 1) {
1099 ArrayList keyParts = new ArrayList ();
1101 XmlNodeList properties = node.SelectNodes ("properties/property");
1102 foreach (XmlNode property in properties) {
1103 XmlAttributeCollection attrs = property.Attributes;
1104 if (attrs["value"] != null) {
1105 keyParts.Add (attrs["name"].Value + "=" + attrs["value"].Value);
1107 keyParts.Add (attrs["name"].Value + "=");
1111 // sort properties by name, as order of properties in XML is
1115 // insert name (type) of attribute
1116 keyParts.Insert (0, name);
1118 StringBuilder sb = new StringBuilder ();
1119 foreach (string value in keyParts) {
1123 key = sb.ToString ();
1131 protected override void LoadExtraData(string name, XmlNode node)
1133 XmlNode pNode = node.SelectSingleNode ("properties");
1135 if (name.EndsWith ("TODOAttribute")) {
1137 if (pNode.ChildNodes [0].Attributes ["value"] != null) {
1138 comment = pNode.ChildNodes [0].Attributes ["value"].Value;
1143 if (pNode != null) {
1144 XMLAttributeProperties p = new XMLAttributeProperties (name);
1147 properties[name] = p;
1151 public override string GroupName {
1152 get { return "attributes"; }
1155 public override string Name {
1156 get { return "attribute"; }
1159 public bool IsTodo {
1160 get { return isTodo; }
1163 public string Comment {
1164 get { return comment; }
1168 class XMLInterfaces : XMLNameGroup
1170 public override string GroupName {
1171 get { return "interfaces"; }
1174 public override string Name {
1175 get { return "interface"; }
1179 class XMLGenericParameters : XMLNameGroup
1182 XMLGenericParameterConstraints constraints;
1184 public override string GroupName {
1185 get { return "generic-parameters"; }
1188 public override string Name {
1189 get { return "generic-parameters"; }
1192 protected override void LoadExtraData (string name, XmlNode node)
1194 attributes = ((XmlElement) node).GetAttribute ("attributes");
1196 var child = node.FirstChild;
1197 if (child != null && child.Name == "generic-parameter-constraints") {
1198 constraints = new XMLGenericParameterConstraints ();
1199 constraints.LoadData (child);
1204 class XMLGenericParameterConstraints : XMLNameGroup
1206 public override string GroupName {
1207 get { return "generic-parameter-constraints"; }
1210 public override string Name {
1211 get { return "generic-parameter-constraint"; }
1215 abstract class XMLMember : XMLNameGroup
1217 Hashtable attributeMap;
1218 Hashtable access = new Hashtable ();
1220 protected override void LoadExtraData (string name, XmlNode node)
1222 XmlAttribute xatt = node.Attributes ["attrib"];
1224 access [name] = xatt.Value;
1226 XmlNode orig = node;
1228 node = node.FirstChild;
1229 while (node != null) {
1230 if (node != null && node.Name == "attributes") {
1231 XMLAttributes a = new XMLAttributes ();
1233 if (attributeMap == null)
1234 attributeMap = new Hashtable ();
1236 attributeMap [name] = a;
1239 node = node.NextSibling;
1242 base.LoadExtraData (name, orig);
1245 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1247 base.CompareToInner (name, parent, other);
1248 XMLMember mb = other as XMLMember;
1249 XMLAttributes att = null;
1250 XMLAttributes oatt = null;
1251 if (attributeMap != null)
1252 att = attributeMap [name] as XMLAttributes;
1254 if (mb != null && mb.attributeMap != null)
1255 oatt = mb.attributeMap [name] as XMLAttributes;
1257 if (att != null || oatt != null) {
1259 att = new XMLAttributes ();
1261 att.CompareTo (document, parent, oatt);
1262 counters.AddPartialToPartial(att.Counters);
1263 if (oatt != null && oatt.IsTodo) {
1265 counters.ErrorTotal++;
1266 AddAttribute (parent, "error", "todo");
1267 if (oatt.Comment != null)
1268 AddAttribute (parent, "comment", oatt.Comment);
1272 XMLMember member = (XMLMember) other;
1273 string acc = access [name] as string;
1278 if (member.access != null)
1279 oacc = member.access [name] as string;
1281 string accName = ConvertToString (Int32.Parse (acc));
1282 string oaccName = "";
1284 oaccName = ConvertToString (Int32.Parse (oacc));
1286 if (accName != oaccName)
1287 AddWarning (parent, "Incorrect attributes: '{0}' != '{1}'", accName, oaccName);
1290 protected virtual string ConvertToString (int att)
1296 class XMLFields : XMLMember
1298 Hashtable fieldTypes;
1299 Hashtable fieldValues;
1301 protected override void LoadExtraData (string name, XmlNode node)
1303 XmlAttribute xatt = node.Attributes ["fieldtype"];
1305 if (fieldTypes == null)
1306 fieldTypes = new Hashtable ();
1308 fieldTypes [name] = xatt.Value;
1311 xatt = node.Attributes ["value"];
1313 if (fieldValues == null)
1314 fieldValues = new Hashtable ();
1316 fieldValues[name] = xatt.Value;
1319 base.LoadExtraData (name, node);
1322 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1324 base.CompareToInner (name, parent, other);
1325 XMLFields fields = (XMLFields) other;
1326 if (fieldTypes != null) {
1327 string ftype = fieldTypes [name] as string;
1328 string oftype = null;
1329 if (fields.fieldTypes != null)
1330 oftype = fields.fieldTypes [name] as string;
1332 if (ftype != oftype)
1333 AddWarning (parent, "Field type is {0} and should be {1}", oftype, ftype);
1335 if (fieldValues != null) {
1336 string fvalue = fieldValues [name] as string;
1337 string ofvalue = null;
1338 if (fields.fieldValues != null)
1339 ofvalue = fields.fieldValues [name] as string;
1341 if (fvalue != ofvalue) {
1342 AddWarning (parent, "Field value is {0} and should be {1}", ofvalue, /*TODO: fvalue (fix 0xFFFF bug)*/ 0);
1347 protected override string ConvertToString (int att)
1349 FieldAttributes fa = (FieldAttributes) att;
1350 return fa.ToString ();
1353 public override string GroupName {
1354 get { return "fields"; }
1357 public override string Name {
1358 get { return "field"; }
1362 class XMLParameters : XMLNameGroup
1364 public override void LoadData (XmlNode node)
1367 throw new ArgumentNullException ("node");
1369 if (node.Name != GroupName)
1370 throw new FormatException (String.Format ("Expecting <{0}>", GroupName));
1372 keys = new Hashtable ();
1373 foreach (XmlNode n in node.ChildNodes) {
1374 string name = n.Attributes["name"].Value;
1375 string key = GetNodeKey (name, n);
1376 XMLParameter parm = new XMLParameter ();
1378 keys.Add (key, parm);
1379 LoadExtraData (key, n);
1383 public override string GroupName {
1385 return "parameters";
1389 public override string Name {
1395 public override string GetNodeKey (string name, XmlNode node)
1397 return node.Attributes["position"].Value;
1400 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
1402 this.document = doc;
1404 group = doc.CreateElement (GroupName, null);
1406 Hashtable okeys = null;
1407 if (other != null && ((XMLParameters) other).keys != null) {
1408 okeys = ((XMLParameters) other).keys;
1411 XmlNode node = null;
1412 bool onull = (okeys == null);
1414 foreach (DictionaryEntry entry in keys) {
1415 node = doc.CreateElement (Name, null);
1416 group.AppendChild (node);
1417 string key = (string) entry.Key;
1418 XMLParameter parm = (XMLParameter) entry.Value;
1419 AddAttribute (node, "name", parm.Name);
1421 if (!onull && HasKey (key, okeys)) {
1422 parm.CompareTo (document, node, okeys[key]);
1423 counters.AddPartialToPartial (parm.Counters);
1427 AddAttribute (node, "presence", "missing");
1433 if (!onull && okeys.Count != 0) {
1434 foreach (XMLParameter value in okeys.Values) {
1435 node = doc.CreateElement (Name, null);
1436 AddAttribute (node, "name", value.Name);
1437 AddAttribute (node, "presence", "extra");
1438 group.AppendChild (node);
1443 if (group.HasChildNodes)
1444 parent.AppendChild (group);
1448 class XMLProperties : XMLMember
1450 Hashtable nameToMethod = new Hashtable ();
1452 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1454 Counters copy = counters;
1455 counters = new Counters();
1457 XMLProperties oprop = other as XMLProperties;
1458 if (oprop != null) {
1459 XMLMethods m = nameToMethod [name] as XMLMethods;
1460 XMLMethods om = oprop.nameToMethod [name] as XMLMethods;
1461 if (m != null || om != null) {
1463 m = new XMLMethods ();
1465 m.CompareTo(document, parent, om);
1466 counters.AddPartialToPartial(m.Counters);
1470 base.CompareToInner (name, parent, other);
1471 AddCountersAttributes(parent);
1473 copy.AddPartialToPartial(counters);
1477 protected override void LoadExtraData (string name, XmlNode node)
1479 XmlNode orig = node;
1480 node = node.FirstChild;
1481 while (node != null) {
1482 if (node != null && node.Name == "methods") {
1483 XMLMethods m = new XMLMethods ();
1484 XmlNode parent = node.ParentNode;
1485 string key = GetNodeKey (name, parent);
1487 nameToMethod [key] = m;
1490 node = node.NextSibling;
1493 base.LoadExtraData (name, orig);
1496 public override string GetNodeKey (string name, XmlNode node)
1498 XmlAttributeCollection atts = node.Attributes;
1499 return String.Format ("{0}:{1}:{2}", atts ["name"].Value,
1500 atts ["ptype"].Value,
1501 atts ["params"].Value);
1504 public override string GroupName {
1505 get { return "properties"; }
1508 public override string Name {
1509 get { return "property"; }
1513 class XMLEvents : XMLMember
1515 Hashtable eventTypes;
1517 protected override void LoadExtraData (string name, XmlNode node)
1519 XmlAttribute xatt = node.Attributes ["eventtype"];
1521 if (eventTypes == null)
1522 eventTypes = new Hashtable ();
1524 eventTypes [name] = xatt.Value;
1527 base.LoadExtraData (name, node);
1530 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1532 Counters copy = counters;
1533 counters = new Counters ();
1536 base.CompareToInner (name, parent, other);
1537 AddCountersAttributes (parent);
1538 if (eventTypes == null)
1541 XMLEvents evt = (XMLEvents) other;
1542 string etype = eventTypes [name] as string;
1543 string oetype = null;
1544 if (evt.eventTypes != null)
1545 oetype = evt.eventTypes [name] as string;
1547 if (etype != oetype)
1548 AddWarning (parent, "Event type is {0} and should be {1}", oetype, etype);
1550 AddCountersAttributes (parent);
1551 copy.AddPartialToPartial (counters);
1556 protected override string ConvertToString (int att)
1558 EventAttributes ea = (EventAttributes) att;
1559 return ea.ToString ();
1562 public override string GroupName {
1563 get { return "events"; }
1566 public override string Name {
1567 get { return "event"; }
1571 class XMLMethods : XMLMember
1573 Hashtable returnTypes;
1574 Hashtable parameters;
1575 Hashtable genericParameters;
1576 Hashtable signatureFlags;
1587 protected override void LoadExtraData (string name, XmlNode node)
1589 XmlAttribute xatt = node.Attributes ["returntype"];
1591 if (returnTypes == null)
1592 returnTypes = new Hashtable ();
1594 returnTypes [name] = xatt.Value;
1597 SignatureFlags flags = SignatureFlags.None;
1598 if (((XmlElement) node).GetAttribute ("abstract") == "true")
1599 flags |= SignatureFlags.Abstract;
1600 if (((XmlElement) node).GetAttribute ("static") == "true")
1601 flags |= SignatureFlags.Static;
1602 if (((XmlElement) node).GetAttribute ("virtual") == "true")
1603 flags |= SignatureFlags.Virtual;
1604 if (flags != SignatureFlags.None) {
1605 if (signatureFlags == null)
1606 signatureFlags = new Hashtable ();
1607 signatureFlags [name] = flags;
1610 XmlNode parametersNode = node.SelectSingleNode ("parameters");
1611 if (parametersNode != null) {
1612 if (parameters == null)
1613 parameters = new Hashtable ();
1615 XMLParameters parms = new XMLParameters ();
1616 parms.LoadData (parametersNode);
1618 parameters[name] = parms;
1621 XmlNode genericNode = node.SelectSingleNode ("generic-parameters");
1622 if (genericNode != null) {
1623 if (genericParameters == null)
1624 genericParameters = new Hashtable ();
1626 XMLGenericParameters gparams = new XMLGenericParameters ();
1627 gparams.LoadData (genericNode);
1628 genericParameters [name] = gparams;
1631 base.LoadExtraData (name, node);
1634 public override string GetNodeKey (string name, XmlNode node)
1636 // for explicit/implicit operators we need to include the return
1637 // type in the key to allow matching; as a side-effect, differences
1638 // in return types will be reported as extra/missing methods
1640 // for regular methods we do not need to take into account the
1641 // return type for matching methods; differences in return types
1642 // will be reported as a warning on the method
1643 if (name.StartsWith ("op_")) {
1644 XmlAttribute xatt = node.Attributes ["returntype"];
1645 string returnType = xatt != null ? xatt.Value + " " : string.Empty;
1646 return returnType + name;
1651 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1653 // create backup of actual counters
1654 Counters copy = counters;
1655 // initialize counters for current method
1656 counters = new Counters();
1659 base.CompareToInner(name, parent, other);
1660 XMLMethods methods = (XMLMethods) other;
1662 SignatureFlags flags = signatureFlags != null &&
1663 signatureFlags.ContainsKey (name) ?
1664 (SignatureFlags) signatureFlags [name] :
1665 SignatureFlags.None;
1666 SignatureFlags oflags = methods.signatureFlags != null &&
1667 methods.signatureFlags.ContainsKey (name) ?
1668 (SignatureFlags) methods.signatureFlags [name] :
1669 SignatureFlags.None;
1671 if (flags!= oflags) {
1672 if (flags == SignatureFlags.None)
1673 AddWarning (parent, String.Format ("should not be {0}", oflags));
1674 else if (oflags == SignatureFlags.None)
1675 AddWarning (parent, String.Format ("should be {0}", flags));
1677 AddWarning (parent, String.Format ("{0} and should be {1}", oflags, flags));
1680 if (returnTypes != null) {
1681 string rtype = returnTypes[name] as string;
1682 string ortype = null;
1683 if (methods.returnTypes != null)
1684 ortype = methods.returnTypes[name] as string;
1686 if (rtype != ortype)
1687 AddWarning (parent, "Return type is {0} and should be {1}", ortype, rtype);
1690 if (parameters != null) {
1691 XMLParameters parms = parameters[name] as XMLParameters;
1692 parms.CompareTo (document, parent, methods.parameters[name]);
1693 counters.AddPartialToPartial (parms.Counters);
1696 // output counter attributes in result document
1697 AddCountersAttributes(parent);
1699 // add temporary counters to actual counters
1700 copy.AddPartialToPartial(counters);
1701 // restore backup of actual counters
1706 protected override string ConvertToString (int att)
1708 MethodAttributes ma = (MethodAttributes) att;
1709 // ignore ReservedMasks
1710 //ma &= ~ MethodAttributes.ReservedMask;
1711 ma &= ~ MethodAttributes.VtableLayoutMask;
1712 if ((ma & MethodAttributes.FamORAssem) != 0)
1713 ma = (ma & ~ MethodAttributes.FamORAssem) | MethodAttributes.Family;
1715 // ignore the HasSecurity attribute for now
1716 if ((ma & MethodAttributes.HasSecurity) != 0)
1717 ma = (MethodAttributes) (att - (int) MethodAttributes.HasSecurity);
1719 // ignore the RequireSecObject attribute for now
1720 if ((ma & MethodAttributes.RequireSecObject) != 0)
1721 ma = (MethodAttributes) (att - (int) MethodAttributes.RequireSecObject);
1723 // we don't care if the implementation is forwarded through PInvoke
1724 if ((ma & MethodAttributes.PInvokeImpl) != 0)
1725 ma = (MethodAttributes) (att - (int) MethodAttributes.PInvokeImpl);
1727 return ma.ToString ();
1730 public override string GroupName {
1731 get { return "methods"; }
1734 public override string Name {
1735 get { return "method"; }
1739 class XMLConstructors : XMLMethods
1741 public override string GroupName {
1742 get { return "constructors"; }
1745 public override string Name {
1746 get { return "constructor"; }
1750 class XmlNodeComparer : IComparer
1752 public static XmlNodeComparer Default = new XmlNodeComparer ();
1754 public int Compare (object a, object b)
1756 XmlNode na = (XmlNode) a;
1757 XmlNode nb = (XmlNode) b;
1758 return String.Compare (na.Attributes ["name"].Value, nb.Attributes ["name"].Value);