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)
10 // C.J. Adams-Collier (cjac@colliertech.org)
12 // (C) 2003 Novell, Inc (http://www.novell.com)
13 // (C) 2009,2010 Collier Technologies (http://www.colliertech.org)
16 using System.Collections;
18 using System.Reflection;
22 namespace Mono.AssemblyCompare
26 static int Main (string [] args)
28 if (args.Length != 2) {
29 Console.WriteLine ("Usage: mono mono-api-diff.exe <assembly 1 xml> <assembly 2 xml>");
33 XMLAssembly ms = CreateXMLAssembly (args [0]);
34 XMLAssembly mono = CreateXMLAssembly (args [1]);
35 XmlDocument doc = ms.CompareAndGetDocument (mono);
37 XmlTextWriter writer = new XmlTextWriter (Console.Out);
38 writer.Formatting = Formatting.Indented;
44 static XMLAssembly CreateXMLAssembly (string file)
46 XmlDocument doc = new XmlDocument ();
47 doc.Load (File.OpenRead (file));
49 XmlNode node = doc.SelectSingleNode ("/assemblies/assembly");
50 XMLAssembly result = new XMLAssembly ();
52 result.LoadData (node);
53 } catch (Exception e) {
54 Console.Error.WriteLine ("Error loading {0}: {1}\n{2}", file, e.Message, e);
65 public int PresentTotal;
67 public int MissingTotal;
72 public int ExtraTotal;
74 public int WarningTotal;
75 public int ErrorTotal;
81 public void AddPartialToPartial (Counters other)
83 Present += other.Present;
85 Missing += other.Missing;
88 Warning += other.Warning;
89 AddPartialToTotal (other);
92 public void AddPartialToTotal (Counters other)
94 PresentTotal += other.Present;
95 ExtraTotal += other.Extra;
96 MissingTotal += other.Missing;
98 TodoTotal += other.Todo;
99 WarningTotal += other.Warning;
102 public void AddTotalToPartial (Counters other)
104 Present += other.PresentTotal;
105 Extra += other.ExtraTotal;
106 Missing += other.MissingTotal;
108 Todo += other.TodoTotal;
109 Warning += other.WarningTotal;
110 AddTotalToTotal (other);
113 public void AddTotalToTotal (Counters other)
115 PresentTotal += other.PresentTotal;
116 ExtraTotal += other.ExtraTotal;
117 MissingTotal += other.MissingTotal;
119 TodoTotal += other.TodoTotal;
120 WarningTotal += other.WarningTotal;
121 ErrorTotal += other.ErrorTotal;
125 get { return Present + Missing; }
128 public int AbsTotal {
129 get { return PresentTotal + MissingTotal; }
133 get { return Present - Todo; }
137 get { return PresentTotal - TodoTotal - ErrorTotal; }
140 public override string ToString ()
142 StringWriter sw = new StringWriter ();
143 sw.WriteLine ("Present: {0}", Present);
144 sw.WriteLine ("PresentTotal: {0}", PresentTotal);
145 sw.WriteLine ("Missing: {0}", Missing);
146 sw.WriteLine ("MissingTotal: {0}", MissingTotal);
147 sw.WriteLine ("Todo: {0}", Todo);
148 sw.WriteLine ("TodoTotal: {0}", TodoTotal);
149 sw.WriteLine ("Extra: {0}", Extra);
150 sw.WriteLine ("ExtraTotal: {0}", ExtraTotal);
151 sw.WriteLine ("Warning: {0}", Warning);
152 sw.WriteLine ("WarningTotal: {0}", WarningTotal);
153 sw.WriteLine ("ErrorTotal: {0}", ErrorTotal);
155 return sw.GetStringBuilder ().ToString ();
159 abstract class XMLData
161 protected XmlDocument document;
162 protected Counters counters;
167 counters = new Counters ();
170 public virtual void LoadData (XmlNode node)
174 protected object [] LoadRecursive (XmlNodeList nodeList, Type type)
176 ArrayList list = new ArrayList ();
177 foreach (XmlNode node in nodeList) {
178 XMLData data = (XMLData) Activator.CreateInstance (type);
179 data.LoadData (node);
183 return (object []) list.ToArray (type);
186 public static bool IsMeaninglessAttribute (string s)
190 if (s == "System.Runtime.CompilerServices.CompilerGeneratedAttribute")
195 public static bool IsMonoTODOAttribute (string s)
199 if (//s.EndsWith ("MonoTODOAttribute") ||
200 s.EndsWith ("MonoDocumentationNoteAttribute") ||
201 s.EndsWith ("MonoExtensionAttribute") ||
202 // s.EndsWith ("MonoInternalNoteAttribute") ||
203 s.EndsWith ("MonoLimitationAttribute") ||
204 s.EndsWith ("MonoNotSupportedAttribute"))
206 return s.EndsWith ("TODOAttribute");
209 protected void AddAttribute (XmlNode node, string name, string value)
211 XmlAttribute attr = document.CreateAttribute (name);
213 node.Attributes.Append (attr);
216 protected void AddExtra (XmlNode node)
218 //TODO: count all the subnodes?
219 AddAttribute (node, "presence", "extra");
220 AddAttribute (node, "ok", "1");
221 AddAttribute (node, "ok_total", "1");
222 AddAttribute (node, "extra", "1");
223 AddAttribute (node, "extra_total", "1");
226 public void AddCountersAttributes (XmlNode node)
228 if (counters.Missing > 0)
229 AddAttribute (node, "missing", counters.Missing.ToString ());
231 if (counters.Present > 0)
232 AddAttribute (node, "present", counters.Present.ToString ());
234 if (counters.Extra > 0)
235 AddAttribute (node, "extra", counters.Extra.ToString ());
238 AddAttribute (node, "ok", counters.Ok.ToString ());
240 if (counters.Total > 0) {
241 int percent = (100 * counters.Ok / counters.Total);
242 AddAttribute (node, "complete", percent.ToString ());
245 if (counters.Todo > 0)
246 AddAttribute (node, "todo", counters.Todo.ToString ());
248 if (counters.Warning > 0)
249 AddAttribute (node, "warning", counters.Warning.ToString ());
251 if (counters.MissingTotal > 0)
252 AddAttribute (node, "missing_total", counters.MissingTotal.ToString ());
254 if (counters.PresentTotal > 0)
255 AddAttribute (node, "present_total", counters.PresentTotal.ToString ());
257 if (counters.ExtraTotal > 0)
258 AddAttribute (node, "extra_total", counters.ExtraTotal.ToString ());
260 if (counters.OkTotal > 0)
261 AddAttribute (node, "ok_total", counters.OkTotal.ToString ());
263 if (counters.AbsTotal > 0) {
264 int percent = (100 * counters.OkTotal / counters.AbsTotal);
265 AddAttribute (node, "complete_total", percent.ToString ());
268 if (counters.TodoTotal > 0) {
269 AddAttribute (node, "todo_total", counters.TodoTotal.ToString ());
270 //TODO: should be different on error. check error cases in corcompare.
271 AddAttribute (node, "error_total", counters.Todo.ToString ());
274 if (counters.WarningTotal > 0)
275 AddAttribute (node, "warning_total", counters.WarningTotal.ToString ());
279 protected void AddWarning (XmlNode parent, string fmt, params object [] args)
283 XmlNode warnings = parent.SelectSingleNode ("warnings");
284 if (warnings == null) {
285 warnings = document.CreateElement ("warnings", null);
286 parent.AppendChild (warnings);
289 AddAttribute (parent, "error", "warning");
290 XmlNode warning = document.CreateElement ("warning", null);
291 AddAttribute (warning, "text", String.Format (fmt, args));
292 warnings.AppendChild (warning);
295 public bool HaveWarnings {
296 get { return haveWarnings; }
299 public Counters Counters {
300 get { return counters; }
303 public abstract void CompareTo (XmlDocument doc, XmlNode parent, object other);
306 abstract class XMLNameGroup : XMLData
308 protected XmlNode group;
309 protected Hashtable keys;
311 public override void LoadData (XmlNode node)
314 throw new ArgumentNullException ("node");
316 if (node.Name != GroupName)
317 throw new FormatException (String.Format ("Expecting <{0}>", GroupName));
319 keys = new Hashtable ();
320 foreach (XmlNode n in node.ChildNodes) {
321 string name = n.Attributes ["name"].Value;
322 if (CheckIfAdd (name, n)) {
323 string key = GetNodeKey (name, n);
324 //keys.Add (key, name);
326 LoadExtraData (key, n);
331 protected virtual bool CheckIfAdd (string value, XmlNode node)
336 protected virtual void LoadExtraData (string name, XmlNode node)
340 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
344 group = doc.CreateElement (GroupName, null);
346 Hashtable okeys = null;
347 if (other != null && ((XMLNameGroup) other).keys != null) {
348 okeys = ((XMLNameGroup) other).keys;
352 bool onull = (okeys == null);
354 foreach (DictionaryEntry entry in keys) {
355 node = doc.CreateElement (Name, null);
356 group.AppendChild (node);
357 string key = (string) entry.Key;
358 string name = (string) entry.Value;
359 AddAttribute (node, "name", name);
361 if (!onull && HasKey (key, okeys)) {
362 CompareToInner (key, node, (XMLNameGroup) other);
366 AddAttribute (node, "presence", "missing");
372 if (!onull && okeys.Count != 0) {
373 foreach (string value in okeys.Values) {
374 node = doc.CreateElement (Name, null);
375 AddAttribute (node, "name", (string) value);
376 AddAttribute (node, "presence", "extra");
377 group.AppendChild (node);
382 if (group.HasChildNodes)
383 parent.AppendChild (group);
386 protected virtual void CompareToInner (string name, XmlNode node, XMLNameGroup other)
390 public virtual string GetNodeKey (string name, XmlNode node)
395 public virtual bool HasKey (string key, Hashtable other)
397 return other.ContainsKey (key);
400 public abstract string GroupName { get; }
401 public abstract string Name { get; }
404 class XMLAssembly : XMLData
406 XMLAttributes attributes;
407 XMLNamespace [] namespaces;
411 public override void LoadData (XmlNode node)
414 throw new ArgumentNullException ("node");
416 name = node.Attributes ["name"].Value;
417 version = node.Attributes ["version"].Value;
418 XmlNode atts = node.FirstChild;
419 attributes = new XMLAttributes ();
420 if (atts.Name == "attributes") {
421 attributes.LoadData (atts);
422 atts = atts.NextSibling;
425 if (atts == null || atts.Name != "namespaces") {
426 Console.Error.WriteLine ("Warning: no namespaces found!");
430 namespaces = (XMLNamespace []) LoadRecursive (atts.ChildNodes, typeof (XMLNamespace));
433 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
435 XMLAssembly assembly = (XMLAssembly) other;
437 XmlNode childA = doc.CreateElement ("assembly", null);
438 AddAttribute (childA, "name", name);
439 AddAttribute (childA, "version", version);
440 if (name != assembly.name)
441 AddWarning (childA, "Assembly names not equal: {0}, {1}", name, assembly.name);
443 if (version != assembly.version)
444 AddWarning (childA, "Assembly version not equal: {0}, {1}", version, assembly.version);
446 parent.AppendChild (childA);
448 attributes.CompareTo (doc, childA, assembly.attributes);
449 counters.AddPartialToPartial (attributes.Counters);
451 CompareNamespaces (childA, assembly.namespaces);
452 if (assembly.attributes != null && assembly.attributes.IsTodo) {
454 counters.TodoTotal++;
455 counters.ErrorTotal++;
456 AddAttribute (childA, "error", "todo");
457 if (assembly.attributes.Comment != null)
458 AddAttribute (childA, "comment", assembly.attributes.Comment);
461 AddCountersAttributes (childA);
464 void CompareNamespaces (XmlNode parent, XMLNamespace [] other)
466 ArrayList newNS = new ArrayList ();
467 XmlNode group = document.CreateElement ("namespaces", null);
468 parent.AppendChild (group);
470 Hashtable oh = CreateHash (other);
472 int count = (namespaces == null) ? 0 : namespaces.Length;
473 for (int i = 0; i < count; i++) {
474 XMLNamespace xns = namespaces [i];
476 node = document.CreateElement ("namespace", null);
478 AddAttribute (node, "name", xns.Name);
481 if (oh.ContainsKey (xns.Name))
482 idx = (int) oh [xns.Name];
483 XMLNamespace ons = idx >= 0 ? (XMLNamespace) other [idx] : null;
484 xns.CompareTo (document, node, ons);
487 xns.AddCountersAttributes (node);
489 counters.PresentTotal++;
490 counters.AddPartialToTotal (xns.Counters);
494 count = other.Length;
495 for (int i = 0; i < count; i++) {
496 XMLNamespace n = other [i];
500 node = document.CreateElement ("namespace", null);
502 AddAttribute (node, "name", n.Name);
504 counters.ExtraTotal++;
508 XmlNode [] nodes = (XmlNode []) newNS.ToArray (typeof (XmlNode));
509 Array.Sort (nodes, XmlNodeComparer.Default);
510 foreach (XmlNode nn in nodes)
511 group.AppendChild (nn);
514 static Hashtable CreateHash (XMLNamespace [] other)
516 Hashtable result = new Hashtable ();
519 foreach (XMLNamespace n in other) {
520 result [n.Name] = i++;
527 public XmlDocument CompareAndGetDocument (XMLAssembly other)
529 XmlDocument doc = new XmlDocument ();
531 XmlNode parent = doc.CreateElement ("assemblies", null);
532 doc.AppendChild (parent);
534 CompareTo (doc, parent, other);
536 XmlNode decl = doc.CreateXmlDeclaration ("1.0", null, null);
537 doc.InsertBefore (decl, doc.DocumentElement);
543 class XMLNamespace : XMLData
548 public override void LoadData (XmlNode node)
551 throw new ArgumentNullException ("node");
553 if (node.Name != "namespace")
554 throw new FormatException ("Expecting <namespace>");
556 name = node.Attributes ["name"].Value;
557 XmlNode classes = node.FirstChild;
558 if (classes == null) {
559 Console.Error.WriteLine ("Warning: no classes for {0}", node.Attributes ["name"]);
563 if (classes.Name != "classes")
564 throw new FormatException ("Expecting <classes>. Got <" + classes.Name + ">");
566 types = (XMLClass []) LoadRecursive (classes.ChildNodes, typeof (XMLClass));
569 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
572 XMLNamespace nspace = (XMLNamespace) other;
574 XmlNode childA = doc.CreateElement ("classes", null);
575 parent.AppendChild (childA);
577 CompareTypes (childA, nspace != null ? nspace.types : new XMLClass [0]);
580 void CompareTypes (XmlNode parent, XMLClass [] other)
582 ArrayList newNodes = new ArrayList ();
583 Hashtable oh = CreateHash (other);
585 int count = (types == null) ? 0 : types.Length;
586 for (int i = 0; i < count; i++) {
587 XMLClass xclass = types [i];
589 node = document.CreateElement ("class", null);
591 AddAttribute (node, "name", xclass.Name);
592 AddAttribute (node, "type", xclass.Type);
595 if (oh.ContainsKey (xclass.Name))
596 idx = (int) oh [xclass.Name];
597 xclass.CompareTo (document, node, idx >= 0 ? other [idx] : new XMLClass ());
600 counters.AddPartialToPartial (xclass.Counters);
604 count = other.Length;
605 for (int i = 0; i < count; i++) {
606 XMLClass c = other [i];
607 if (c == null || IsMonoTODOAttribute (c.Name))
610 node = document.CreateElement ("class", null);
612 AddAttribute (node, "name", c.Name);
613 AddAttribute (node, "type", c.Type);
616 counters.ExtraTotal++;
620 XmlNode [] nodes = (XmlNode []) newNodes.ToArray (typeof (XmlNode));
621 Array.Sort (nodes, XmlNodeComparer.Default);
622 foreach (XmlNode nn in nodes)
623 parent.AppendChild (nn);
626 static Hashtable CreateHash (XMLClass [] other)
628 Hashtable result = new Hashtable ();
631 foreach (XMLClass c in other) {
632 result [c.Name] = i++;
644 class XMLClass : XMLData
654 XMLAttributes attributes;
655 XMLInterfaces interfaces;
656 XMLGenericTypeConstraints genericConstraints;
658 XMLConstructors constructors;
659 XMLProperties properties;
664 public override void LoadData (XmlNode node)
667 throw new ArgumentNullException ("node");
669 name = node.Attributes ["name"].Value;
670 type = node.Attributes ["type"].Value;
671 XmlAttribute xatt = node.Attributes ["base"];
673 baseName = xatt.Value;
675 xatt = node.Attributes ["sealed"];
676 isSealed = (xatt != null && xatt.Value == "true");
678 xatt = node.Attributes ["abstract"];
679 isAbstract = (xatt != null && xatt.Value == "true");
681 xatt = node.Attributes["serializable"];
682 isSerializable = (xatt != null && xatt.Value == "true");
684 xatt = node.Attributes["charset"];
686 charSet = xatt.Value;
688 xatt = node.Attributes["layout"];
692 XmlNode child = node.FirstChild;
694 // Console.Error.WriteLine ("Empty class {0} {1}", name, type);
698 if (child.Name == "attributes") {
699 attributes = new XMLAttributes ();
700 attributes.LoadData (child);
701 child = child.NextSibling;
704 if (child != null && child.Name == "interfaces") {
705 interfaces = new XMLInterfaces ();
706 interfaces.LoadData (child);
707 child = child.NextSibling;
710 if (child != null && child.Name == "generic-type-constraints") {
711 genericConstraints = new XMLGenericTypeConstraints ();
712 genericConstraints.LoadData (child);
713 child = child.NextSibling;
716 if (child != null && child.Name == "fields") {
717 fields = new XMLFields ();
718 fields.LoadData (child);
719 child = child.NextSibling;
722 if (child != null && child.Name == "constructors") {
723 constructors = new XMLConstructors ();
724 constructors.LoadData (child);
725 child = child.NextSibling;
728 if (child != null && child.Name == "properties") {
729 properties = new XMLProperties ();
730 properties.LoadData (child);
731 child = child.NextSibling;
734 if (child != null && child.Name == "events") {
735 events = new XMLEvents ();
736 events.LoadData (child);
737 child = child.NextSibling;
740 if (child != null && child.Name == "methods") {
741 methods = new XMLMethods ();
742 methods.LoadData (child);
743 child = child.NextSibling;
746 if (child != null && child.Name == "generic-parameters") {
747 // HACK: ignore this tag as it doesn't seem to
748 // add any value when checking for differences
755 if (child.Name != "classes") {
756 Console.WriteLine ("name: {0} type: {1} {2}", name, type, child.NodeType);
757 throw new FormatException ("Expecting <classes>. Got <" + child.Name + ">");
760 nested = (XMLClass []) LoadRecursive (child.ChildNodes, typeof (XMLClass));
763 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
766 XMLClass oclass = (XMLClass) other;
768 if (attributes != null || oclass.attributes != null) {
769 if (attributes == null)
770 attributes = new XMLAttributes ();
772 attributes.CompareTo (doc, parent, oclass.attributes);
773 counters.AddPartialToPartial (attributes.Counters);
774 if (oclass.attributes != null && oclass.attributes.IsTodo) {
776 counters.TodoTotal++;
777 counters.ErrorTotal++;
778 AddAttribute (parent, "error", "todo");
779 if (oclass.attributes.Comment != null)
780 AddAttribute (parent, "comment", oclass.attributes.Comment);
784 if (type != oclass.type)
785 AddWarning (parent, "Class type is wrong: {0} != {1}", type, oclass.type);
787 if (baseName != oclass.baseName)
788 AddWarning (parent, "Base class is wrong: {0} != {1}", baseName, oclass.baseName);
790 if (isAbstract != oclass.isAbstract || isSealed != oclass.isSealed) {
791 if ((isAbstract && isSealed) || (oclass.isAbstract && oclass.isSealed))
792 AddWarning (parent, "Should {0}be static", (isAbstract && isSealed) ? "" : "not ");
793 else if (isAbstract != oclass.isAbstract)
794 AddWarning (parent, "Should {0}be abstract", isAbstract ? "" : "not ");
795 else if (isSealed != oclass.isSealed)
796 AddWarning (parent, "Should {0}be sealed", isSealed ? "" : "not ");
799 if (isSerializable != oclass.isSerializable)
800 AddWarning (parent, "Should {0}be serializable", isSerializable ? "" : "not ");
802 if (charSet != oclass.charSet)
803 AddWarning (parent, "CharSet is wrong: {0} != {1}", charSet, oclass.charSet);
805 if (layout != oclass.layout)
806 AddWarning (parent, "Layout is wrong: {0} != {1}", layout, oclass.layout);
808 if (interfaces != null || oclass.interfaces != null) {
809 if (interfaces == null)
810 interfaces = new XMLInterfaces ();
812 interfaces.CompareTo (doc, parent, oclass.interfaces);
813 counters.AddPartialToPartial (interfaces.Counters);
816 if (genericConstraints != null || oclass.genericConstraints != null) {
817 if (genericConstraints == null)
818 genericConstraints = new XMLGenericTypeConstraints ();
820 genericConstraints.CompareTo (doc, parent, oclass.genericConstraints);
821 counters.AddPartialToPartial (genericConstraints.Counters);
824 if (fields != null || oclass.fields != null) {
826 fields = new XMLFields ();
828 fields.CompareTo (doc, parent, oclass.fields);
829 counters.AddPartialToPartial (fields.Counters);
832 if (constructors != null || oclass.constructors != null) {
833 if (constructors == null)
834 constructors = new XMLConstructors ();
836 constructors.CompareTo (doc, parent, oclass.constructors);
837 counters.AddPartialToPartial (constructors.Counters);
840 if (properties != null || oclass.properties != null) {
841 if (properties == null)
842 properties = new XMLProperties ();
844 properties.CompareTo (doc, parent, oclass.properties);
845 counters.AddPartialToPartial (properties.Counters);
848 if (events != null || oclass.events != null) {
850 events = new XMLEvents ();
852 events.CompareTo (doc, parent, oclass.events);
853 counters.AddPartialToPartial (events.Counters);
856 if (methods != null || oclass.methods != null) {
858 methods = new XMLMethods ();
860 methods.CompareTo (doc, parent, oclass.methods);
861 counters.AddPartialToPartial (methods.Counters);
864 if (nested != null || oclass.nested != null) {
865 XmlNode n = doc.CreateElement ("classes", null);
866 parent.AppendChild (n);
867 CompareTypes (n, oclass.nested);
870 AddCountersAttributes (parent);
873 void CompareTypes (XmlNode parent, XMLClass [] other)
875 ArrayList newNodes = new ArrayList ();
876 Hashtable oh = CreateHash (other);
878 int count = (nested == null) ? 0 : nested.Length;
879 for (int i = 0; i < count; i++) {
880 XMLClass xclass = nested [i];
882 node = document.CreateElement ("class", null);
884 AddAttribute (node, "name", xclass.Name);
885 AddAttribute (node, "type", xclass.Type);
887 if (oh.ContainsKey (xclass.Name)) {
888 int idx = (int) oh [xclass.Name];
889 xclass.CompareTo (document, node, other [idx]);
891 counters.AddPartialToPartial (xclass.Counters);
893 // TODO: Should I count here?
894 AddAttribute (node, "presence", "missing");
896 counters.MissingTotal++;
901 count = other.Length;
902 for (int i = 0; i < count; i++) {
903 XMLClass c = other [i];
904 if (c == null || IsMonoTODOAttribute (c.Name))
907 node = document.CreateElement ("class", null);
909 AddAttribute (node, "name", c.Name);
910 AddAttribute (node, "type", c.Type);
913 counters.ExtraTotal++;
917 XmlNode [] nodes = (XmlNode []) newNodes.ToArray (typeof (XmlNode));
918 Array.Sort (nodes, XmlNodeComparer.Default);
919 foreach (XmlNode nn in nodes)
920 parent.AppendChild (nn);
923 static Hashtable CreateHash (XMLClass [] other)
925 Hashtable result = new Hashtable ();
928 foreach (XMLClass c in other) {
929 result [c.Name] = i++;
945 class XMLParameter : XMLData
954 XMLAttributes attributes;
956 public override void LoadData (XmlNode node)
959 throw new ArgumentNullException ("node");
961 if (node.Name != "parameter")
962 throw new ArgumentException ("Expecting <parameter>");
964 name = node.Attributes["name"].Value;
965 type = node.Attributes["type"].Value;
966 attrib = node.Attributes["attrib"].Value;
967 if (node.Attributes ["direction"] != null)
968 direction = node.Attributes["direction"].Value;
969 if (node.Attributes["unsafe"] != null)
970 isUnsafe = bool.Parse (node.Attributes["unsafe"].Value);
971 if (node.Attributes["optional"] != null)
972 isOptional = bool.Parse (node.Attributes["optional"].Value);
973 if (node.Attributes["defaultValue"] != null)
974 defaultValue = node.Attributes["defaultValue"].Value;
976 XmlNode child = node.FirstChild;
980 if (child.Name == "attributes") {
981 attributes = new XMLAttributes ();
982 attributes.LoadData (child);
983 child = child.NextSibling;
987 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
991 XMLParameter oparm = (XMLParameter) other;
993 if (name != oparm.name)
994 AddWarning (parent, "Parameter name is wrong: {0} != {1}", name, oparm.name);
996 if (type != oparm.type)
997 AddWarning (parent, "Parameter type is wrong: {0} != {1}", type, oparm.type);
999 if (attrib != oparm.attrib)
1000 AddWarning (parent, "Parameter attributes wrong: {0} != {1}", attrib, oparm.attrib);
1002 if (direction != oparm.direction)
1003 AddWarning (parent, "Parameter direction wrong: {0} != {1}", direction, oparm.direction);
1005 if (isUnsafe != oparm.isUnsafe)
1006 AddWarning (parent, "Parameter unsafe wrong: {0} != {1}", isUnsafe, oparm.isUnsafe);
1008 if (isOptional != oparm.isOptional)
1009 AddWarning (parent, "Parameter optional wrong: {0} != {1}", isOptional, oparm.isOptional);
1011 if (defaultValue != oparm.defaultValue)
1012 AddWarning (parent, "Parameter default value wrong: {0} != {1}", (defaultValue == null) ? "(no default value)" : defaultValue, (oparm.defaultValue == null) ? "(no default value)" : oparm.defaultValue);
1014 if (attributes != null || oparm.attributes != null) {
1015 if (attributes == null)
1016 attributes = new XMLAttributes ();
1018 attributes.CompareTo (doc, parent, oparm.attributes);
1019 counters.AddPartialToPartial (attributes.Counters);
1020 if (oparm.attributes != null && oparm.attributes.IsTodo) {
1022 counters.TodoTotal++;
1023 counters.ErrorTotal++;
1024 AddAttribute (parent, "error", "todo");
1025 if (oparm.attributes.Comment != null)
1026 AddAttribute (parent, "comment", oparm.attributes.Comment);
1031 public string Name {
1032 get { return name; }
1036 class XMLAttributeProperties: XMLNameGroup
1038 static Hashtable ignored_properties;
1039 static XMLAttributeProperties ()
1041 ignored_properties = new Hashtable ();
1042 ignored_properties.Add ("System.Reflection.AssemblyKeyFileAttribute", "KeyFile");
1043 ignored_properties.Add ("System.Reflection.AssemblyCompanyAttribute", "Company");
1044 ignored_properties.Add ("System.Reflection.AssemblyConfigurationAttribute", "Configuration");
1045 ignored_properties.Add ("System.Reflection.AssemblyCopyrightAttribute", "Copyright");
1046 ignored_properties.Add ("System.Reflection.AssemblyProductAttribute", "Product");
1047 ignored_properties.Add ("System.Reflection.AssemblyTrademarkAttribute", "Trademark");
1048 ignored_properties.Add ("System.Reflection.AssemblyInformationalVersionAttribute", "InformationalVersion");
1050 ignored_properties.Add ("System.ObsoleteAttribute", "Message");
1051 ignored_properties.Add ("System.IO.IODescriptionAttribute", "Description");
1052 ignored_properties.Add ("System.Diagnostics.MonitoringDescriptionAttribute", "Description");
1055 Hashtable properties = new Hashtable ();
1058 public XMLAttributeProperties (string attribute)
1060 this.attribute = attribute;
1063 public override void LoadData(XmlNode node)
1066 throw new ArgumentNullException ("node");
1068 if (node.ChildNodes == null)
1071 string ignored = ignored_properties [attribute] as string;
1073 foreach (XmlNode n in node.ChildNodes) {
1074 string name = n.Attributes ["name"].Value;
1075 if (ignored == name)
1078 if (n.Attributes ["null"] != null) {
1079 properties.Add (name, null);
1082 string value = n.Attributes ["value"].Value;
1083 properties.Add (name, value);
1087 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
1089 this.document = doc;
1091 Hashtable other_properties = ((XMLAttributeProperties)other).properties;
1092 foreach (DictionaryEntry de in other_properties) {
1093 object other_value = properties [de.Key];
1095 if (de.Value == null) {
1096 if (other_value != null)
1097 AddWarning (parent, "Property '{0}' is 'null' and should be '{1}'", de.Key, other_value);
1101 if (de.Value.Equals (other_value))
1104 AddWarning (parent, "Property '{0}' is '{1}' and should be '{2}'",
1105 de.Key, de.Value, other_value == null ? "null" : other_value);
1109 public override string GroupName {
1111 return "properties";
1115 public override string Name {
1122 class XMLAttributes : XMLNameGroup
1124 Hashtable properties = new Hashtable ();
1129 protected override bool CheckIfAdd (string value, XmlNode node)
1131 if (IsMonoTODOAttribute (value)) {
1134 XmlNode pNode = node.SelectSingleNode ("properties");
1135 if (pNode != null && pNode.ChildNodes.Count > 0 && pNode.ChildNodes [0].Attributes ["value"] != null) {
1136 comment = pNode.ChildNodes [0].Attributes ["value"].Value;
1141 return !IsMeaninglessAttribute (value);
1144 protected override void CompareToInner (string name, XmlNode node, XMLNameGroup other)
1146 XMLAttributeProperties other_prop = ((XMLAttributes)other).properties [name] as XMLAttributeProperties;
1147 XMLAttributeProperties this_prop = properties [name] as XMLAttributeProperties;
1148 if (other_prop == null || this_prop == null)
1151 this_prop.CompareTo (document, node, other_prop);
1152 counters.AddPartialToPartial (this_prop.Counters);
1155 public override string GetNodeKey (string name, XmlNode node)
1159 // if multiple attributes with the same name (type) exist, then we
1160 // cannot be sure which attributes correspond, so we must use the
1161 // name of the attribute (type) and the name/value of its properties
1164 XmlNodeList attributes = node.ParentNode.SelectNodes("attribute[@name='" + name + "']");
1165 if (attributes.Count > 1) {
1166 ArrayList keyParts = new ArrayList ();
1168 XmlNodeList properties = node.SelectNodes ("properties/property");
1169 foreach (XmlNode property in properties) {
1170 XmlAttributeCollection attrs = property.Attributes;
1171 if (attrs["value"] != null) {
1172 keyParts.Add (attrs["name"].Value + "=" + attrs["value"].Value);
1174 keyParts.Add (attrs["name"].Value + "=");
1178 // sort properties by name, as order of properties in XML is
1182 // insert name (type) of attribute
1183 keyParts.Insert (0, name);
1185 StringBuilder sb = new StringBuilder ();
1186 foreach (string value in keyParts) {
1190 key = sb.ToString ();
1198 protected override void LoadExtraData(string name, XmlNode node)
1200 XmlNode pNode = node.SelectSingleNode ("properties");
1202 if (IsMonoTODOAttribute (name)) {
1204 if (pNode.ChildNodes [0].Attributes ["value"] != null) {
1205 comment = pNode.ChildNodes [0].Attributes ["value"].Value;
1210 if (pNode != null) {
1211 XMLAttributeProperties p = new XMLAttributeProperties (name);
1214 properties[name] = p;
1218 public override string GroupName {
1219 get { return "attributes"; }
1222 public override string Name {
1223 get { return "attribute"; }
1226 public bool IsTodo {
1227 get { return isTodo; }
1230 public string Comment {
1231 get { return comment; }
1235 class XMLInterfaces : XMLNameGroup
1237 public override string GroupName {
1238 get { return "interfaces"; }
1241 public override string Name {
1242 get { return "interface"; }
1246 abstract class XMLGenericGroup : XMLNameGroup
1250 protected override void LoadExtraData (string name, XmlNode node)
1252 attributes = ((XmlElement) node).GetAttribute ("generic-attribute");
1255 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1257 base.CompareToInner (name, parent, other);
1259 XMLGenericGroup g = (XMLGenericGroup) other;
1260 if (attributes != g.attributes)
1261 AddWarning (parent, "Incorrect generic attributes: '{0}' != '{1}'", attributes, g.attributes);
1265 class XMLGenericTypeConstraints : XMLGenericGroup
1267 public override string GroupName {
1268 get { return "generic-type-constraints"; }
1271 public override string Name {
1272 get { return "generic-type-constraint"; }
1276 class XMLGenericMethodConstraints : XMLGenericGroup
1278 public override string GroupName {
1279 get { return "generic-method-constraints"; }
1282 public override string Name {
1283 get { return "generic-method-constraint"; }
1287 abstract class XMLMember : XMLNameGroup
1289 Hashtable attributeMap;
1290 Hashtable access = new Hashtable ();
1292 protected override void LoadExtraData (string name, XmlNode node)
1294 XmlAttribute xatt = node.Attributes ["attrib"];
1296 access [name] = xatt.Value;
1298 XmlNode orig = node;
1300 node = node.FirstChild;
1301 while (node != null) {
1302 if (node != null && node.Name == "attributes") {
1303 XMLAttributes a = new XMLAttributes ();
1305 if (attributeMap == null)
1306 attributeMap = new Hashtable ();
1308 attributeMap [name] = a;
1311 node = node.NextSibling;
1314 base.LoadExtraData (name, orig);
1317 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1319 base.CompareToInner (name, parent, other);
1320 XMLMember mb = other as XMLMember;
1321 XMLAttributes att = null;
1322 XMLAttributes oatt = null;
1323 if (attributeMap != null)
1324 att = attributeMap [name] as XMLAttributes;
1326 if (mb != null && mb.attributeMap != null)
1327 oatt = mb.attributeMap [name] as XMLAttributes;
1329 if (att != null || oatt != null) {
1331 att = new XMLAttributes ();
1333 att.CompareTo (document, parent, oatt);
1334 counters.AddPartialToPartial(att.Counters);
1335 if (oatt != null && oatt.IsTodo) {
1337 counters.ErrorTotal++;
1338 AddAttribute (parent, "error", "todo");
1339 if (oatt.Comment != null)
1340 AddAttribute (parent, "comment", oatt.Comment);
1344 XMLMember member = (XMLMember) other;
1345 string acc = access [name] as string;
1350 if (member.access != null)
1351 oacc = member.access [name] as string;
1353 string accName = ConvertToString (Int32.Parse (acc));
1354 string oaccName = "";
1356 oaccName = ConvertToString (Int32.Parse (oacc));
1358 if (accName != oaccName)
1359 AddWarning (parent, "Incorrect attributes: '{0}' != '{1}'", accName, oaccName);
1362 protected virtual string ConvertToString (int att)
1368 class XMLFields : XMLMember
1370 Hashtable fieldTypes;
1371 Hashtable fieldValues;
1373 protected override void LoadExtraData (string name, XmlNode node)
1375 XmlAttribute xatt = node.Attributes ["fieldtype"];
1377 if (fieldTypes == null)
1378 fieldTypes = new Hashtable ();
1380 fieldTypes [name] = xatt.Value;
1383 xatt = node.Attributes ["value"];
1385 if (fieldValues == null)
1386 fieldValues = new Hashtable ();
1388 fieldValues[name] = xatt.Value;
1391 base.LoadExtraData (name, node);
1394 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1396 base.CompareToInner (name, parent, other);
1397 XMLFields fields = (XMLFields) other;
1398 if (fieldTypes != null) {
1399 string ftype = fieldTypes [name] as string;
1400 string oftype = null;
1401 if (fields.fieldTypes != null)
1402 oftype = fields.fieldTypes [name] as string;
1404 if (ftype != oftype)
1405 AddWarning (parent, "Field type is {0} and should be {1}", oftype, ftype);
1407 if (fieldValues != null) {
1408 string fvalue = fieldValues [name] as string;
1409 string ofvalue = null;
1410 if (fields.fieldValues != null)
1411 ofvalue = fields.fieldValues [name] as string;
1413 if (fvalue != ofvalue)
1414 AddWarning (parent, "Field value is {0} and should be {1}", ofvalue, fvalue);
1418 protected override string ConvertToString (int att)
1420 FieldAttributes fa = (FieldAttributes) att;
1421 return fa.ToString ();
1424 public override string GroupName {
1425 get { return "fields"; }
1428 public override string Name {
1429 get { return "field"; }
1433 class XMLParameters : XMLNameGroup
1435 public override void LoadData (XmlNode node)
1438 throw new ArgumentNullException ("node");
1440 if (node.Name != GroupName)
1441 throw new FormatException (String.Format ("Expecting <{0}>", GroupName));
1443 keys = new Hashtable ();
1444 foreach (XmlNode n in node.ChildNodes) {
1445 string name = n.Attributes["name"].Value;
1446 string key = GetNodeKey (name, n);
1447 XMLParameter parm = new XMLParameter ();
1449 keys.Add (key, parm);
1450 LoadExtraData (key, n);
1454 public override string GroupName {
1456 return "parameters";
1460 public override string Name {
1466 public override string GetNodeKey (string name, XmlNode node)
1468 return node.Attributes["position"].Value;
1471 public override void CompareTo (XmlDocument doc, XmlNode parent, object other)
1473 this.document = doc;
1475 group = doc.CreateElement (GroupName, null);
1477 Hashtable okeys = null;
1478 if (other != null && ((XMLParameters) other).keys != null) {
1479 okeys = ((XMLParameters) other).keys;
1482 XmlNode node = null;
1483 bool onull = (okeys == null);
1485 foreach (DictionaryEntry entry in keys) {
1486 node = doc.CreateElement (Name, null);
1487 group.AppendChild (node);
1488 string key = (string) entry.Key;
1489 XMLParameter parm = (XMLParameter) entry.Value;
1490 AddAttribute (node, "name", parm.Name);
1492 if (!onull && HasKey (key, okeys)) {
1493 parm.CompareTo (document, node, okeys[key]);
1494 counters.AddPartialToPartial (parm.Counters);
1498 AddAttribute (node, "presence", "missing");
1504 if (!onull && okeys.Count != 0) {
1505 foreach (XMLParameter value in okeys.Values) {
1506 node = doc.CreateElement (Name, null);
1507 AddAttribute (node, "name", value.Name);
1508 AddAttribute (node, "presence", "extra");
1509 group.AppendChild (node);
1514 if (group.HasChildNodes)
1515 parent.AppendChild (group);
1519 class XMLProperties : XMLMember
1521 Hashtable nameToMethod = new Hashtable ();
1523 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1525 Counters copy = counters;
1526 counters = new Counters();
1528 XMLProperties oprop = other as XMLProperties;
1529 if (oprop != null) {
1530 XMLMethods m = nameToMethod [name] as XMLMethods;
1531 XMLMethods om = oprop.nameToMethod [name] as XMLMethods;
1532 if (m != null || om != null) {
1534 m = new XMLMethods ();
1536 m.CompareTo(document, parent, om);
1537 counters.AddPartialToPartial(m.Counters);
1541 base.CompareToInner (name, parent, other);
1542 AddCountersAttributes(parent);
1544 copy.AddPartialToPartial(counters);
1548 protected override void LoadExtraData (string name, XmlNode node)
1550 XmlNode orig = node;
1551 node = node.FirstChild;
1552 while (node != null) {
1553 if (node != null && node.Name == "methods") {
1554 XMLMethods m = new XMLMethods ();
1555 XmlNode parent = node.ParentNode;
1556 string key = GetNodeKey (name, parent);
1558 nameToMethod [key] = m;
1561 node = node.NextSibling;
1564 base.LoadExtraData (name, orig);
1567 public override string GetNodeKey (string name, XmlNode node)
1569 XmlAttributeCollection atts = node.Attributes;
1570 return String.Format ("{0}:{1}:{2}",
1571 (atts["name"] != null ? atts["name"].Value : ""),
1572 (atts["ptype"] != null ? atts["ptype"].Value : ""),
1573 (atts["params"] != null ? atts["params"].Value : "")
1577 public override string GroupName {
1578 get { return "properties"; }
1581 public override string Name {
1582 get { return "property"; }
1586 class XMLEvents : XMLMember
1588 Hashtable eventTypes;
1589 Hashtable nameToMethod = new Hashtable ();
1591 protected override void LoadExtraData (string name, XmlNode node)
1593 XmlAttribute xatt = node.Attributes ["eventtype"];
1595 if (eventTypes == null)
1596 eventTypes = new Hashtable ();
1598 eventTypes [name] = xatt.Value;
1601 XmlNode child = node.FirstChild;
1602 while (child != null) {
1603 if (child != null && child.Name == "methods") {
1604 XMLMethods m = new XMLMethods ();
1605 XmlNode parent = child.ParentNode;
1606 string key = GetNodeKey (name, parent);
1608 nameToMethod [key] = m;
1611 child = child.NextSibling;
1614 base.LoadExtraData (name, node);
1617 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1619 Counters copy = counters;
1620 counters = new Counters ();
1623 base.CompareToInner (name, parent, other);
1624 AddCountersAttributes (parent);
1625 if (eventTypes == null)
1628 XMLEvents evt = (XMLEvents) other;
1629 string etype = eventTypes [name] as string;
1630 string oetype = null;
1631 if (evt.eventTypes != null)
1632 oetype = evt.eventTypes [name] as string;
1634 if (etype != oetype)
1635 AddWarning (parent, "Event type is {0} and should be {1}", oetype, etype);
1637 XMLMethods m = nameToMethod [name] as XMLMethods;
1638 XMLMethods om = evt.nameToMethod [name] as XMLMethods;
1639 if (m != null || om != null) {
1641 m = new XMLMethods ();
1643 m.CompareTo (document, parent, om);
1644 counters.AddPartialToPartial (m.Counters);
1647 AddCountersAttributes (parent);
1648 copy.AddPartialToPartial (counters);
1653 protected override string ConvertToString (int att)
1655 EventAttributes ea = (EventAttributes) att;
1656 return ea.ToString ();
1659 public override string GroupName {
1660 get { return "events"; }
1663 public override string Name {
1664 get { return "event"; }
1668 class XMLMethods : XMLMember
1670 Hashtable returnTypes;
1671 Hashtable parameters;
1672 Hashtable genericConstraints;
1673 Hashtable signatureFlags;
1685 protected override void LoadExtraData (string name, XmlNode node)
1687 XmlAttribute xatt = node.Attributes ["returntype"];
1689 if (returnTypes == null)
1690 returnTypes = new Hashtable ();
1692 returnTypes [name] = xatt.Value;
1695 SignatureFlags flags = SignatureFlags.None;
1696 if (((XmlElement) node).GetAttribute ("abstract") == "true")
1697 flags |= SignatureFlags.Abstract;
1698 if (((XmlElement) node).GetAttribute ("static") == "true")
1699 flags |= SignatureFlags.Static;
1700 if (((XmlElement) node).GetAttribute ("virtual") == "true")
1701 flags |= SignatureFlags.Virtual;
1702 if (((XmlElement) node).GetAttribute ("final") == "true")
1703 flags |= SignatureFlags.Final;
1704 if (flags != SignatureFlags.None) {
1705 if (signatureFlags == null)
1706 signatureFlags = new Hashtable ();
1707 signatureFlags [name] = flags;
1710 XmlNode parametersNode = node.SelectSingleNode ("parameters");
1711 if (parametersNode != null) {
1712 if (parameters == null)
1713 parameters = new Hashtable ();
1715 XMLParameters parms = new XMLParameters ();
1716 parms.LoadData (parametersNode);
1718 parameters[name] = parms;
1721 XmlNode genericNode = node.SelectSingleNode ("generic-method-constraints");
1722 if (genericNode != null) {
1723 if (genericConstraints == null)
1724 genericConstraints = new Hashtable ();
1725 XMLGenericMethodConstraints csts = new XMLGenericMethodConstraints ();
1726 csts.LoadData (genericNode);
1727 genericConstraints [name] = csts;
1730 base.LoadExtraData (name, node);
1733 public override string GetNodeKey (string name, XmlNode node)
1735 // for explicit/implicit operators we need to include the return
1736 // type in the key to allow matching; as a side-effect, differences
1737 // in return types will be reported as extra/missing methods
1739 // for regular methods we do not need to take into account the
1740 // return type for matching methods; differences in return types
1741 // will be reported as a warning on the method
1742 if (name.StartsWith ("op_")) {
1743 XmlAttribute xatt = node.Attributes ["returntype"];
1744 string returnType = xatt != null ? xatt.Value + " " : string.Empty;
1745 return returnType + name;
1750 protected override void CompareToInner (string name, XmlNode parent, XMLNameGroup other)
1752 // create backup of actual counters
1753 Counters copy = counters;
1754 // initialize counters for current method
1755 counters = new Counters();
1758 base.CompareToInner(name, parent, other);
1759 XMLMethods methods = (XMLMethods) other;
1761 SignatureFlags flags = signatureFlags != null &&
1762 signatureFlags.ContainsKey (name) ?
1763 (SignatureFlags) signatureFlags [name] :
1764 SignatureFlags.None;
1765 SignatureFlags oflags = methods.signatureFlags != null &&
1766 methods.signatureFlags.ContainsKey (name) ?
1767 (SignatureFlags) methods.signatureFlags [name] :
1768 SignatureFlags.None;
1770 if (flags!= oflags) {
1771 if (flags == SignatureFlags.None)
1772 AddWarning (parent, String.Format ("should not be {0}", oflags));
1773 else if (oflags == SignatureFlags.None)
1774 AddWarning (parent, String.Format ("should be {0}", flags));
1776 AddWarning (parent, String.Format ("{0} and should be {1}", oflags, flags));
1779 if (returnTypes != null) {
1780 string rtype = returnTypes[name] as string;
1781 string ortype = null;
1782 if (methods.returnTypes != null)
1783 ortype = methods.returnTypes[name] as string;
1785 if (rtype != ortype)
1786 AddWarning (parent, "Return type is {0} and should be {1}", ortype, rtype);
1789 if (parameters != null) {
1790 XMLParameters parms = parameters[name] as XMLParameters;
1791 parms.CompareTo (document, parent, methods.parameters[name]);
1792 counters.AddPartialToPartial (parms.Counters);
1795 // output counter attributes in result document
1796 AddCountersAttributes(parent);
1798 // add temporary counters to actual counters
1799 copy.AddPartialToPartial(counters);
1800 // restore backup of actual counters
1805 protected override string ConvertToString (int att)
1807 MethodAttributes ma = (MethodAttributes) att;
1808 // ignore ReservedMasks
1809 ma &= ~ MethodAttributes.ReservedMask;
1810 ma &= ~ MethodAttributes.VtableLayoutMask;
1811 if ((ma & MethodAttributes.FamORAssem) != 0)
1812 ma = (ma & ~ MethodAttributes.FamORAssem) | MethodAttributes.Family;
1814 // ignore the HasSecurity attribute for now
1815 if ((ma & MethodAttributes.HasSecurity) != 0)
1816 ma = (MethodAttributes) (att - (int) MethodAttributes.HasSecurity);
1818 // ignore the RequireSecObject attribute for now
1819 if ((ma & MethodAttributes.RequireSecObject) != 0)
1820 ma = (MethodAttributes) (att - (int) MethodAttributes.RequireSecObject);
1822 // we don't care if the implementation is forwarded through PInvoke
1823 if ((ma & MethodAttributes.PinvokeImpl) != 0)
1824 ma = (MethodAttributes) (att - (int) MethodAttributes.PinvokeImpl);
1826 return ma.ToString ();
1829 public override string GroupName {
1830 get { return "methods"; }
1833 public override string Name {
1834 get { return "method"; }
1838 class XMLConstructors : XMLMethods
1840 public override string GroupName {
1841 get { return "constructors"; }
1844 public override string Name {
1845 get { return "constructor"; }
1849 class XmlNodeComparer : IComparer
1851 public static XmlNodeComparer Default = new XmlNodeComparer ();
1853 public int Compare (object a, object b)
1855 XmlNode na = (XmlNode) a;
1856 XmlNode nb = (XmlNode) b;
1857 return String.Compare (na.Attributes ["name"].Value, nb.Attributes ["name"].Value);