2005-03-11 Sebastien Pouliot <sebastien@ximian.com>
[mono.git] / mcs / tools / corcompare / mono-api-info.cs
1 //
2 // mono-api-info.cs - Dumps public assembly information to an xml file.
3 //
4 // Authors:
5 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 //
7 // Copyright (C) 2003-2005 Novell, Inc (http://www.novell.com)
8 //
9
10 using System;
11 using System.Collections;
12 using System.Globalization;
13 using System.Reflection;
14 using System.Runtime.InteropServices;
15 using System.Security.Permissions;
16 using System.Text;
17 using System.Xml;
18
19 namespace Mono.AssemblyInfo
20 {
21         class Driver
22         {
23                 static int Main (string [] args)
24                 {
25                         if (args.Length == 0)
26                                 return 1;
27
28                         AssemblyCollection acoll = new AssemblyCollection ();
29                         
30                         foreach (string fullName in args) {
31                                 acoll.Add (fullName);
32                         }
33
34                         XmlDocument doc = new XmlDocument ();
35                         acoll.Document = doc;
36                         acoll.DoOutput ();
37
38                         XmlTextWriter writer = new XmlTextWriter (Console.Out);
39                         writer.Formatting = Formatting.Indented;
40                         XmlNode decl = doc.CreateXmlDeclaration ("1.0", null, null);
41                         doc.InsertBefore (decl, doc.DocumentElement);
42                         doc.WriteTo (writer);
43                         return 0;
44                 }
45         }
46
47         class AssemblyCollection
48         {
49                 XmlDocument document;
50                 ArrayList assemblies;
51
52                 public AssemblyCollection ()
53                 {
54                         assemblies = new ArrayList ();
55                 }
56
57                 public bool Add (string name)
58                 {
59                         Assembly ass = LoadAssembly (name);
60                         if (ass == null)
61                                 return false;
62
63                         assemblies.Add (ass);
64                         return true;
65                 }
66
67                 public void DoOutput ()
68                 {
69                         if (document == null)
70                                 throw new InvalidOperationException ("Document not set");
71
72                         XmlNode nassemblies = document.CreateElement ("assemblies", null);
73                         document.AppendChild (nassemblies);
74                         foreach (Assembly a in assemblies) {
75                                 AssemblyData data = new AssemblyData (document, nassemblies, a);
76                                 data.DoOutput ();
77                         }
78                 }
79
80                 public XmlDocument Document {
81                         set { document = value; }
82                 }
83                 
84                 static Assembly LoadAssembly (string aname)
85                 {
86                         Assembly ass = null;
87                         try {
88                                 string name = aname;
89                                 if (!name.EndsWith (".dll"))
90                                         name += ".dll";
91                                 ass = Assembly.LoadFrom (name);
92                                 return ass;
93                         } catch { }
94
95                         try {
96                                 ass = Assembly.LoadWithPartialName (aname);
97                                 return ass;
98                         } catch { }
99
100                         return null;
101                 }
102         }
103
104         abstract class BaseData
105         {
106                 protected XmlDocument document;
107                 protected XmlNode parent;
108
109                 protected BaseData (XmlDocument doc, XmlNode parent)
110                 {
111                         this.document = doc;
112                         this.parent = parent;
113                 }
114
115                 public abstract void DoOutput ();
116
117                 protected void AddAttribute (XmlNode node, string name, string value)
118                 {
119                         XmlAttribute attr = document.CreateAttribute (name);
120                         attr.Value = value;
121                         node.Attributes.Append (attr);
122                 }
123         }
124
125         class AssemblyData : BaseData
126         {
127                 Assembly ass;
128                 
129                 public AssemblyData (XmlDocument document, XmlNode parent, Assembly ass)
130                         : base (document, parent)
131                 {
132                         this.ass = ass;
133                 }
134
135                 public override void DoOutput ()
136                 {
137                         if (document == null)
138                                 throw new InvalidOperationException ("Document not set");
139
140                         XmlNode nassembly = document.CreateElement ("assembly", null);
141                         AssemblyName aname = ass.GetName ();
142                         AddAttribute (nassembly, "name", aname.Name);
143                         AddAttribute (nassembly, "version", aname.Version.ToString ());
144                         parent.AppendChild (nassembly);
145                         AttributeData.OutputAttributes (document, nassembly, ass.GetCustomAttributes (false));
146                         Type [] types = ass.GetExportedTypes ();
147                         if (types == null || types.Length == 0)
148                                 return;
149
150                         Array.Sort (types, TypeComparer.Default);
151
152                         XmlNode nss = document.CreateElement ("namespaces", null);
153                         nassembly.AppendChild (nss);
154
155                         string currentNS = "$%&$&";
156                         XmlNode ns = null;
157                         XmlNode classes = null;
158                         foreach (Type t in types) {
159                                 if (t.Namespace == null || t.Namespace == "")
160                                         continue;
161
162                                 if (t.IsNotPublic)
163                                         continue;
164
165                                 if (t.IsNestedPublic || t.IsNestedAssembly || t.IsNestedFamANDAssem ||
166                                         t.IsNestedFamORAssem || t.IsNestedPrivate)
167                                         continue;
168
169                                 if (t.DeclaringType != null)
170                                         continue; // enforce !nested
171                                 
172                                 if (t.Namespace != currentNS) {
173                                         currentNS = t.Namespace;
174                                         ns = document.CreateElement ("namespace", null);
175                                         AddAttribute (ns, "name", currentNS);
176                                         nss.AppendChild (ns);
177                                         classes = document.CreateElement ("classes", null);
178                                         ns.AppendChild (classes);
179                                 }
180                                 
181                                 TypeData bd = new TypeData (document, classes, t);
182                                 bd.DoOutput ();
183                         }
184                 }
185         }
186
187         abstract class MemberData : BaseData
188         {
189                 MemberInfo [] members;
190
191                 public MemberData (XmlDocument document, XmlNode parent, MemberInfo [] members)
192                         : base (document, parent)
193                 {
194                         this.members = members;
195                 }
196
197                 public override void DoOutput ()
198                 {
199                         XmlNode mclass = document.CreateElement (ParentTag, null);
200                         parent.AppendChild (mclass);
201
202                         foreach (MemberInfo member in members) {
203                                 XmlNode mnode = document.CreateElement (Tag, null);
204                                 mclass.AppendChild (mnode);
205                                 AddAttribute (mnode, "name", GetName (member));
206                                 if (!NoMemberAttributes)
207                                         AddAttribute (mnode, "attrib", GetMemberAttributes (member));
208
209                                 AttributeData.OutputAttributes (document, mnode,
210                                                                 member.GetCustomAttributes (false));
211
212                                 AddExtraData (mnode, member);
213                         }
214                 }
215
216                 protected virtual void AddExtraData (XmlNode p, MemberInfo member)
217                 {
218                 }
219
220                 protected virtual string GetName (MemberInfo member)
221                 {
222                         return "NoNAME";
223                 }
224
225                 protected virtual string GetMemberAttributes (MemberInfo member)
226                 {
227                         return null;
228                 }
229
230                 public virtual bool NoMemberAttributes {
231                         get { return false; }
232                         set {}
233                 }
234
235                 public virtual string ParentTag {
236                         get { return "NoPARENTTAG"; }
237                 }
238                 
239                 public virtual string Tag {
240                         get { return "NoTAG"; }
241                 }
242         }
243
244         class TypeData : MemberData
245         {
246                 Type type;
247                 const BindingFlags flags = BindingFlags.Public | BindingFlags.Static |
248                                                 BindingFlags.Instance | BindingFlags.DeclaredOnly | 
249                                                 BindingFlags.NonPublic;
250                 
251                 public TypeData (XmlDocument document, XmlNode parent, Type type)
252                         : base (document, parent, null)
253                 {
254                         this.type = type;
255                 }
256
257                 public override void DoOutput ()
258                 {
259                         if (document == null)
260                                 throw new InvalidOperationException ("Document not set");
261
262                         XmlNode nclass = document.CreateElement ("class", null);
263                         AddAttribute (nclass, "name", type.Name);
264                         string classType = GetClassType (type);
265                         AddAttribute (nclass, "type", classType);
266
267                         if (type.BaseType != null)
268                                 AddAttribute (nclass, "base", type.BaseType.FullName);
269
270                         if (type.IsSealed)
271                                 AddAttribute (nclass, "sealed", "true");
272
273                         if (type.IsAbstract)
274                                 AddAttribute (nclass, "abstract", "true");
275
276                         if (type.IsSerializable)
277                                 AddAttribute (nclass, "serializable", "true");
278
279                         string charSet = GetCharSet (type);
280                         AddAttribute (nclass, "charset", charSet);
281
282                         string layout = GetLayout (type);
283                         if (layout != null)
284                                 AddAttribute (nclass, "layout", layout);
285
286                         parent.AppendChild (nclass);
287                         
288                         AttributeData.OutputAttributes (document, nclass, type.GetCustomAttributes (false));
289
290                         Type [] interfaces = type.GetInterfaces ();
291                         if (interfaces != null && interfaces.Length > 0) {
292                                 XmlNode ifaces = document.CreateElement ("interfaces", null);
293                                 nclass.AppendChild (ifaces);
294                                 foreach (Type t in interfaces) {
295                                         if (!t.IsPublic) {
296                                                 // we're only interested in public interfaces
297                                                 continue;
298                                         }
299                                         XmlNode iface = document.CreateElement ("interface", null);
300                                         AddAttribute (iface, "name", t.FullName);
301                                         ifaces.AppendChild (iface);
302                                 }
303                         }
304
305                         ArrayList members = new ArrayList ();
306
307                         FieldInfo[] fields = GetFields (type);
308                         if (fields.Length > 0) {
309                                 Array.Sort (fields, MemberInfoComparer.Default);
310                                 FieldData fd = new FieldData (document, nclass, fields);
311                                 // Special case for enum fields
312                                 if (classType == "enum") {
313                                         string etype = fields [0].GetType ().FullName;
314                                         AddAttribute (nclass, "enumtype", etype);
315                                 }
316                                 members.Add (fd);
317                         }
318
319                         ConstructorInfo [] ctors = GetConstructors (type);
320                         if (ctors.Length > 0) {
321                                 Array.Sort (ctors, MemberInfoComparer.Default);
322                                 members.Add (new ConstructorData (document, nclass, ctors));
323                         }
324
325                         PropertyInfo[] properties = GetProperties (type);
326                         if (properties.Length > 0) {
327                                 Array.Sort (properties, MemberInfoComparer.Default);
328                                 members.Add (new PropertyData (document, nclass, properties));
329                         }
330
331                         EventInfo [] events = GetEvents (type);
332                         if (events.Length > 0) {
333                                 Array.Sort (events, MemberInfoComparer.Default);
334                                 members.Add (new EventData (document, nclass, events));
335                         }
336
337                         MethodInfo [] methods = GetMethods (type);
338                         if (methods.Length > 0) {
339                                 Array.Sort (methods, MemberInfoComparer.Default);
340                                 members.Add (new MethodData (document, nclass, methods));
341                         }
342
343                         foreach (MemberData md in members)
344                                 md.DoOutput ();
345
346                         Type [] nested = type.GetNestedTypes ();
347                         if (nested != null && nested.Length > 0) {
348                                 XmlNode classes = document.CreateElement ("classes", null);
349                                 nclass.AppendChild (classes);
350                                 foreach (Type t in nested) {
351                                         TypeData td = new TypeData (document, classes, t);
352                                         td.DoOutput ();
353                                 }
354                         }
355                 }
356
357                 protected override string GetMemberAttributes (MemberInfo member)
358                 {
359                         if (member != type)
360                                 throw new InvalidOperationException ("odd");
361                                 
362                         return ((int) type.Attributes).ToString (CultureInfo.InvariantCulture);
363                 }
364
365                 public static bool MustDocumentMethod(MethodBase method)
366                 {
367                         // All other methods
368                         return (method.IsPublic || method.IsFamily || method.IsFamilyOrAssembly);
369                 }
370
371                 static string GetClassType (Type t)
372                 {
373                         if (t.IsEnum)
374                                 return "enum";
375
376                         if (t.IsValueType)
377                                 return "struct";
378
379                         if (t.IsInterface)
380                                 return "interface";
381
382                         if (typeof (Delegate).IsAssignableFrom (t))
383                                 return "delegate";
384
385                         return "class";
386                 }
387
388                 private static string GetCharSet (Type type)
389                 {
390                         if (type.IsAnsiClass)
391                                 return CharSet.Ansi.ToString (CultureInfo.InvariantCulture);
392
393                         if (type.IsAutoClass)
394                                 return CharSet.Auto.ToString (CultureInfo.InvariantCulture);
395
396                         if (type.IsUnicodeClass)
397                                 return CharSet.Unicode.ToString (CultureInfo.InvariantCulture);
398
399                         return CharSet.None.ToString (CultureInfo.InvariantCulture);
400                 }
401
402                 private static string GetLayout (Type type)
403                 {
404                         if (type.IsAutoLayout)
405                                 return LayoutKind.Auto.ToString (CultureInfo.InvariantCulture);
406
407                         if (type.IsExplicitLayout)
408                                 return LayoutKind.Explicit.ToString (CultureInfo.InvariantCulture);
409
410                         if (type.IsLayoutSequential)
411                                 return LayoutKind.Sequential.ToString (CultureInfo.InvariantCulture);
412
413                         return null;
414                 }
415
416                 private FieldInfo[] GetFields (Type type)
417                 {
418                         ArrayList list = new ArrayList ();
419
420                         FieldInfo[] fields = type.GetFields (flags);
421                         foreach (FieldInfo field in fields) {
422                                 if (field.IsSpecialName)
423                                         continue;
424
425                                 // we're only interested in public or protected members
426                                 if (!field.IsPublic && !field.IsFamily && !field.IsFamilyOrAssembly)
427                                         continue;
428
429                                 list.Add (field);
430                         }
431
432                         return (FieldInfo[]) list.ToArray (typeof (FieldInfo));
433                 }
434
435                 internal static PropertyInfo[] GetProperties (Type type)
436                 {
437                         ArrayList list = new ArrayList ();
438
439                         PropertyInfo[] properties = type.GetProperties (flags);
440                         foreach (PropertyInfo property in properties) {
441                                 MethodInfo getMethod = null;
442                                 MethodInfo setMethod = null;
443
444                                 if (property.CanRead) {
445                                         try { getMethod = property.GetGetMethod (true); }
446                                         catch (System.Security.SecurityException) { }
447                                 }
448                                 if (property.CanWrite) {
449                                         try { setMethod = property.GetSetMethod (true); }
450                                         catch (System.Security.SecurityException) { }
451                                 }
452
453                                 bool hasGetter = (getMethod != null) && MustDocumentMethod (getMethod);
454                                 bool hasSetter = (setMethod != null) && MustDocumentMethod (setMethod);
455
456                                 // if neither the getter or setter should be documented, then
457                                 // skip the property
458                                 if (!hasGetter && !hasSetter) {
459                                         continue;
460                                 }
461
462                                 list.Add (property);
463                         }
464
465                         return (PropertyInfo[]) list.ToArray (typeof (PropertyInfo));
466                 }
467
468                 private MethodInfo[] GetMethods (Type type)
469                 {
470                         ArrayList list = new ArrayList ();
471
472                         MethodInfo[] methods = type.GetMethods (flags);
473                         foreach (MethodInfo method in methods) {
474                                 if (method.IsSpecialName)
475                                         continue;
476
477                                 // we're only interested in public or protected members
478                                 if (!MustDocumentMethod(method))
479                                         continue;
480
481                                 list.Add (method);
482                         }
483
484                         return (MethodInfo[]) list.ToArray (typeof (MethodInfo));
485                 }
486
487                 private ConstructorInfo[] GetConstructors (Type type)
488                 {
489                         ArrayList list = new ArrayList ();
490
491                         ConstructorInfo[] ctors = type.GetConstructors (flags);
492                         foreach (ConstructorInfo constructor in ctors) {
493                                 // we're only interested in public or protected members
494                                 if (!constructor.IsPublic && !constructor.IsFamily && !constructor.IsFamilyOrAssembly)
495                                         continue;
496
497                                 list.Add (constructor);
498                         }
499
500                         return (ConstructorInfo[]) list.ToArray (typeof (ConstructorInfo));
501                 }
502
503                 private EventInfo[] GetEvents (Type type)
504                 {
505                         ArrayList list = new ArrayList ();
506
507                         EventInfo[] events = type.GetEvents (flags);
508                         foreach (EventInfo eventInfo in events) {
509                                 MethodInfo addMethod = eventInfo.GetAddMethod (true);
510
511                                 if (addMethod == null || !MustDocumentMethod (addMethod))
512                                         continue;
513
514                                 list.Add (eventInfo);
515                         }
516
517                         return (EventInfo[]) list.ToArray (typeof (EventInfo));
518                 }
519         }
520
521         class FieldData : MemberData
522         {
523                 public FieldData (XmlDocument document, XmlNode parent, FieldInfo [] members)
524                         : base (document, parent, members)
525                 {
526                 }
527
528                 protected override string GetName (MemberInfo member)
529                 {
530                         FieldInfo field = (FieldInfo) member;
531                         return field.Name;
532                 }
533
534                 protected override string GetMemberAttributes (MemberInfo member)
535                 {
536                         FieldInfo field = (FieldInfo) member;
537                         return ((int) field.Attributes).ToString (CultureInfo.InvariantCulture);
538                 }
539
540                 protected override void AddExtraData (XmlNode p, MemberInfo member)
541                 {
542                         base.AddExtraData (p, member);
543                         FieldInfo field = (FieldInfo) member;
544                         AddAttribute (p, "fieldtype", field.FieldType.FullName);
545
546                         if (field.IsLiteral) {
547                                 object value = field.GetValue (null);
548                                 string stringValue = null;
549                                 if (value is Enum) {
550                                         // FIXME: when Mono bug #60090 has been
551                                         // fixed, we should just be able to use
552                                         // Convert.ToString
553                                         stringValue = ((Enum) value).ToString ("D", CultureInfo.InvariantCulture);
554                                 } else {
555                                         stringValue = Convert.ToString (value, CultureInfo.InvariantCulture);
556                                 }
557
558                                 if (stringValue != null)
559                                         AddAttribute (p, "value", stringValue);
560                         }
561                 }
562
563                 public override string ParentTag {
564                         get { return "fields"; }
565                 }
566
567                 public override string Tag {
568                         get { return "field"; }
569                 }
570         }
571
572         class PropertyData : MemberData
573         {
574                 public PropertyData (XmlDocument document, XmlNode parent, PropertyInfo [] members)
575                         : base (document, parent, members)
576                 {
577                 }
578
579                 protected override string GetName (MemberInfo member)
580                 {
581                         PropertyInfo prop = (PropertyInfo) member;
582                         return prop.Name;
583                 }
584
585                 protected override void AddExtraData (XmlNode p, MemberInfo member)
586                 {
587                         base.AddExtraData (p, member);
588                         PropertyInfo prop = (PropertyInfo) member;
589                         AddAttribute (p, "ptype", prop.PropertyType.FullName);
590                         MethodInfo _get = prop.GetGetMethod (true);
591                         MethodInfo _set = prop.GetSetMethod (true);
592                         bool haveGet = (_get != null && TypeData.MustDocumentMethod(_get));
593                         bool haveSet = (_set != null && TypeData.MustDocumentMethod(_set));
594                         MethodInfo [] methods;
595
596                         if (haveGet && haveSet) {
597                                 methods = new MethodInfo [] {_get, _set};
598                         } else if (haveGet) {
599                                 methods = new MethodInfo [] {_get};
600                         } else if (haveSet) {
601                                 methods = new MethodInfo [] {_set};
602                         } else {
603                                 //odd
604                                 return;
605                         }
606
607                         string parms = Parameters.GetSignature (methods [0].GetParameters ());
608                         AddAttribute (p, "params", parms);
609
610                         MethodData data = new MethodData (document, p, methods);
611                         data.NoMemberAttributes = true;
612                         data.DoOutput ();
613                 }
614
615                 protected override string GetMemberAttributes (MemberInfo member)
616                 {
617                         PropertyInfo prop = (PropertyInfo) member;
618                         return ((int) prop.Attributes).ToString (CultureInfo.InvariantCulture);
619                 }
620
621                 public override string ParentTag {
622                         get { return "properties"; }
623                 }
624
625                 public override string Tag {
626                         get { return "property"; }
627                 }
628         }
629
630         class EventData : MemberData
631         {
632                 public EventData (XmlDocument document, XmlNode parent, EventInfo [] members)
633                         : base (document, parent, members)
634                 {
635                 }
636
637                 protected override string GetName (MemberInfo member)
638                 {
639                         EventInfo evt = (EventInfo) member;
640                         return evt.Name;
641                 }
642
643                 protected override string GetMemberAttributes (MemberInfo member)
644                 {
645                         EventInfo evt = (EventInfo) member;
646                         return ((int) evt.Attributes).ToString (CultureInfo.InvariantCulture);
647                 }
648
649                 protected override void AddExtraData (XmlNode p, MemberInfo member)
650                 {
651                         base.AddExtraData (p, member);
652                         EventInfo evt = (EventInfo) member;
653                         AddAttribute (p, "eventtype", evt.EventHandlerType.FullName);
654                 }
655
656                 public override string ParentTag {
657                         get { return "events"; }
658                 }
659
660                 public override string Tag {
661                         get { return "event"; }
662                 }
663         }
664
665         class MethodData : MemberData
666         {
667                 bool noAtts;
668
669                 public MethodData (XmlDocument document, XmlNode parent, MethodBase [] members)
670                         : base (document, parent, members)
671                 {
672                 }
673
674                 protected override string GetName (MemberInfo member)
675                 {
676                         MethodBase method = (MethodBase) member;
677                         string name = method.Name;
678                         string parms = Parameters.GetSignature (method.GetParameters ());
679                         return String.Format ("{0}({1})", name, parms);
680                 }
681
682                 protected override string GetMemberAttributes (MemberInfo member)
683                 {
684                         MethodBase method = (MethodBase) member;
685                         return ((int) method.Attributes).ToString (CultureInfo.InvariantCulture);
686                 }
687
688                 protected override void AddExtraData (XmlNode p, MemberInfo member)
689                 {
690                         base.AddExtraData (p, member);
691
692                         ParameterData parms = new ParameterData (document, p, 
693                                 ((MethodBase) member).GetParameters ());
694                         parms.DoOutput ();
695
696                         if (!(member is MethodInfo))
697                                 return;
698
699                         MethodInfo method = (MethodInfo) member;
700                         AddAttribute (p, "returntype", method.ReturnType.FullName);
701
702                         AttributeData.OutputAttributes (document, p,
703                                 method.ReturnTypeCustomAttributes.GetCustomAttributes (false));
704                 }
705
706                 public override bool NoMemberAttributes {
707                         get { return noAtts; }
708                         set { noAtts = value; }
709                 }
710                 
711                 public override string ParentTag {
712                         get { return "methods"; }
713                 }
714
715                 public override string Tag {
716                         get { return "method"; }
717                 }
718         }
719
720         class ConstructorData : MethodData
721         {
722                 public ConstructorData (XmlDocument document, XmlNode parent, ConstructorInfo [] members)
723                         : base (document, parent, members)
724                 {
725                 }
726
727                 public override string ParentTag {
728                         get { return "constructors"; }
729                 }
730
731                 public override string Tag {
732                         get { return "constructor"; }
733                 }
734         }
735
736         class ParameterData : BaseData
737         {
738                 private ParameterInfo[] parameters;
739
740                 public ParameterData (XmlDocument document, XmlNode parent, ParameterInfo[] parameters)
741                         : base (document, parent)
742                 {
743                         this.parameters = parameters;
744                 }
745
746                 public override void DoOutput ()
747                 {
748                         XmlNode parametersNode = document.CreateElement ("parameters", null);
749                         parent.AppendChild (parametersNode);
750
751                         foreach (ParameterInfo parameter in parameters) {
752                                 XmlNode paramNode = document.CreateElement ("parameter", null);
753                                 parametersNode.AppendChild (paramNode);
754                                 AddAttribute (paramNode, "name", parameter.Name);
755                                 AddAttribute (paramNode, "position", parameter.Position.ToString(CultureInfo.InvariantCulture));
756                                 AddAttribute (paramNode, "attrib", ((int) parameter.Attributes).ToString());
757
758                                 string direction = "in";
759
760                                 if (parameter.ParameterType.IsByRef) {
761                                         direction = parameter.IsOut ? "out" : "ref";
762                                 }
763
764                                 Type t = parameter.ParameterType;
765                                 AddAttribute (paramNode, "type", t.FullName);
766
767                                 if (parameter.IsOptional) {
768                                         AddAttribute (paramNode, "optional", "true");
769                                         if (parameter.DefaultValue != null)
770                                                 AddAttribute (paramNode, "defaultValue", parameter.DefaultValue.ToString ());
771                                 }
772
773                                 if (direction != "in")
774                                         AddAttribute (paramNode, "direction", direction);
775
776                                 AttributeData.OutputAttributes (document, paramNode, parameter.GetCustomAttributes (false));
777                         }
778                 }
779         }
780
781         class AttributeData : BaseData
782         {
783                 object [] atts;
784                 string target;
785
786                 AttributeData (XmlDocument doc, XmlNode parent, object[] attributes, string target)
787                         : base (doc, parent)
788                 {
789                         atts = attributes;
790                         this.target = target;
791                 }
792
793                 AttributeData (XmlDocument doc, XmlNode parent, object [] attributes)
794                         : this (doc, parent, attributes, null)
795                 {
796                 }
797
798                 public override void DoOutput ()
799                 {
800                         if (document == null)
801                                 throw new InvalidOperationException ("Document not set");
802
803                         if (atts == null || atts.Length == 0)
804                                 return;
805
806                         XmlNode natts = parent.SelectSingleNode("attributes");
807                         if (natts == null) {
808                                 natts = document.CreateElement ("attributes", null);
809                                 parent.AppendChild (natts);
810                         }
811
812                         for (int i = 0; i < atts.Length; ++i) {
813                                 Type t = atts [i].GetType ();
814                                 if (!t.IsPublic && !t.Name.EndsWith ("TODOAttribute"))
815                                         continue;
816
817                                 // we ignore attributes that inherit from SecurityAttribute on purpose as they:
818                                 // * aren't part of GetCustomAttributes in Fx 1.0/1.1;
819                                 // * are encoded differently and in a different metadata table; and
820                                 // * won't ever exactly match MS implementation (from a syntax pov)
821                                 if (t.IsSubclassOf (typeof (SecurityAttribute)))
822                                         continue;
823
824                                 XmlNode node = document.CreateElement ("attribute");
825                                 AddAttribute (node, "name", t.FullName);
826
827                                 XmlNode properties = null;
828                                 foreach (PropertyInfo pi in TypeData.GetProperties (t)) {
829                                         if (pi.Name == "TypeId")
830                                                 continue;
831
832                                         if (properties == null) {
833                                                 properties = node.AppendChild (document.CreateElement ("properties"));
834                                         }
835
836                                         try {
837                                                 object o = pi.GetValue (atts [i], null);
838
839                                                 XmlNode n = properties.AppendChild (document.CreateElement ("property"));
840                                                 AddAttribute (n, "name", pi.Name);
841
842                                                 if (o == null) {
843                                                         AddAttribute (n, "null", "true");
844                                                         continue;
845                                                 }
846
847                                                 string value = o.ToString ();
848                                                 if (t == typeof (GuidAttribute))
849                                                         value = value.ToUpper ();
850
851                                                 AddAttribute (n, "value", value);
852                                         }
853                                         catch (TargetInvocationException) {
854                                                 continue;
855                                         }
856                                 }
857
858                                 if (target != null) {
859                                         AddAttribute (node, "target", target);
860                                 }
861
862                                 natts.AppendChild (node);
863                         }
864                 }
865
866                 public static void OutputAttributes (XmlDocument doc, XmlNode parent, object[] attributes)
867                 {
868                         AttributeData ad = new AttributeData (doc, parent, attributes, null);
869                         ad.DoOutput ();
870                 }
871
872                 public static void OutputAttributes (XmlDocument doc, XmlNode parent, object [] attributes, string target)
873                 {
874                         AttributeData ad = new AttributeData (doc, parent, attributes, target);
875                         ad.DoOutput ();
876                 }
877
878                 private static bool MustDocumentAttribute (Type attributeType)
879                 {
880                         // only document MonoTODOAttribute and public attributes
881                         return attributeType.Name.EndsWith ("TODOAttribute") || attributeType.IsPublic;
882                 }
883         }
884
885         class Parameters
886         {
887                 private Parameters () {}
888
889                 public static string GetSignature (ParameterInfo [] infos)
890                 {
891                         if (infos == null || infos.Length == 0)
892                                 return "";
893
894                         StringBuilder sb = new StringBuilder ();
895                         foreach (ParameterInfo info in infos) {
896                                 string modifier;
897                                 if (info.IsIn)
898                                         modifier = "in ";
899                                 else if (info.IsRetval)
900                                         modifier = "ref ";
901                                 else if (info.IsOut)
902                                         modifier = "out ";
903                                 else
904                                         modifier = "";
905
906                                 string type_name = info.ParameterType.ToString ();
907                                 sb.AppendFormat ("{0}{1}, ", modifier, type_name);
908                         }
909
910                         sb.Length -= 2; // remove ", "
911                         return sb.ToString ();
912                 }
913
914         }
915         
916         class TypeComparer : IComparer
917         {
918                 public static TypeComparer Default = new TypeComparer ();
919
920                 public int Compare (object a, object b)
921                 {
922                         Type ta = (Type) a;
923                         Type tb = (Type) b;
924                         int result = String.Compare (ta.Namespace, tb.Namespace);
925                         if (result != 0)
926                                 return result;
927
928                         return String.Compare (ta.Name, tb.Name);
929                 }
930         }
931
932         class MemberInfoComparer : IComparer
933         {
934                 public static MemberInfoComparer Default = new MemberInfoComparer ();
935
936                 public int Compare (object a, object b)
937                 {
938                         MemberInfo ma = (MemberInfo) a;
939                         MemberInfo mb = (MemberInfo) b;
940                         return String.Compare (ma.Name, mb.Name);
941                 }
942         }
943
944         class MethodBaseComparer : IComparer
945         {
946                 public static MethodBaseComparer Default = new MethodBaseComparer ();
947
948                 public int Compare (object a, object b)
949                 {
950                         MethodBase ma = (MethodBase) a;
951                         MethodBase mb = (MethodBase) b;
952                         int res = String.Compare (ma.Name, mb.Name);
953                         if (res != 0)
954                                 return res;
955
956                         ParameterInfo [] pia = ma.GetParameters ();
957                         ParameterInfo [] pib = mb.GetParameters ();
958                         if (pia.Length != pib.Length)
959                                 return pia.Length - pib.Length;
960
961                         string siga = Parameters.GetSignature (pia);
962                         string sigb = Parameters.GetSignature (pib);
963                         return String.Compare (siga, sigb);
964                 }
965         }
966 }
967