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