New test.
[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.ToString ());
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.ToString ());
301                                         ifaces.AppendChild (iface);
302                                 }
303                         }
304
305 #if NET_2_0
306                         // Generic constraints
307                         Type [] gargs = type.GetGenericArguments ();
308                         XmlElement ngeneric = (gargs.Length == 0) ? null :
309                                 document.CreateElement ("generic-type-constraints");
310                         foreach (Type garg in gargs) {
311                                 Type [] csts = garg.GetGenericParameterConstraints ();
312                                 if (csts.Length == 0 || csts [0] == typeof (object))
313                                         continue;
314                                 XmlElement el = document.CreateElement ("generic-type-constraint");
315                                 el.SetAttribute ("name", garg.ToString ());
316                                 el.SetAttribute ("generic-attribute",
317                                         garg.GenericParameterAttributes.ToString ());
318                                 ngeneric.AppendChild (el);
319                                 foreach (Type ct in csts) {
320                                         XmlElement cel = document.CreateElement ("type");
321                                         cel.AppendChild (document.CreateTextNode (ct.FullName));
322                                         el.AppendChild (cel);
323                                 }
324                         }
325                         if (ngeneric != null && ngeneric.FirstChild != null)
326                                 nclass.AppendChild (ngeneric);
327 #endif
328
329                         ArrayList members = new ArrayList ();
330
331                         FieldInfo[] fields = GetFields (type);
332                         if (fields.Length > 0) {
333                                 Array.Sort (fields, MemberInfoComparer.Default);
334                                 FieldData fd = new FieldData (document, nclass, fields);
335                                 // Special case for enum fields
336                                 if (classType == "enum") {
337                                         string etype = fields [0].GetType ().ToString ();
338                                         AddAttribute (nclass, "enumtype", etype);
339                                 }
340                                 members.Add (fd);
341                         }
342
343                         ConstructorInfo [] ctors = GetConstructors (type);
344                         if (ctors.Length > 0) {
345                                 Array.Sort (ctors, MemberInfoComparer.Default);
346                                 members.Add (new ConstructorData (document, nclass, ctors));
347                         }
348
349                         PropertyInfo[] properties = GetProperties (type);
350                         if (properties.Length > 0) {
351                                 Array.Sort (properties, MemberInfoComparer.Default);
352                                 members.Add (new PropertyData (document, nclass, properties));
353                         }
354
355                         EventInfo [] events = GetEvents (type);
356                         if (events.Length > 0) {
357                                 Array.Sort (events, MemberInfoComparer.Default);
358                                 members.Add (new EventData (document, nclass, events));
359                         }
360
361                         MethodInfo [] methods = GetMethods (type);
362                         if (methods.Length > 0) {
363                                 Array.Sort (methods, MemberInfoComparer.Default);
364                                 members.Add (new MethodData (document, nclass, methods));
365                         }
366
367                         foreach (MemberData md in members)
368                                 md.DoOutput ();
369
370                         Type [] nested = type.GetNestedTypes ();
371                         if (nested != null && nested.Length > 0) {
372                                 XmlNode classes = document.CreateElement ("classes", null);
373                                 nclass.AppendChild (classes);
374                                 foreach (Type t in nested) {
375                                         TypeData td = new TypeData (document, classes, t);
376                                         td.DoOutput ();
377                                 }
378                         }
379                 }
380
381                 protected override string GetMemberAttributes (MemberInfo member)
382                 {
383                         if (member != type)
384                                 throw new InvalidOperationException ("odd");
385                                 
386                         return ((int) type.Attributes).ToString (CultureInfo.InvariantCulture);
387                 }
388
389                 public static bool MustDocumentMethod(MethodBase method)
390                 {
391                         // All other methods
392                         return (method.IsPublic || method.IsFamily || method.IsFamilyOrAssembly);
393                 }
394
395                 static string GetClassType (Type t)
396                 {
397                         if (t.IsEnum)
398                                 return "enum";
399
400                         if (t.IsValueType)
401                                 return "struct";
402
403                         if (t.IsInterface)
404                                 return "interface";
405
406                         if (typeof (Delegate).IsAssignableFrom (t))
407                                 return "delegate";
408
409                         return "class";
410                 }
411
412                 private static string GetCharSet (Type type)
413                 {
414                         if (type.IsAnsiClass)
415                                 return CharSet.Ansi.ToString (CultureInfo.InvariantCulture);
416
417                         if (type.IsAutoClass)
418                                 return CharSet.Auto.ToString (CultureInfo.InvariantCulture);
419
420                         if (type.IsUnicodeClass)
421                                 return CharSet.Unicode.ToString (CultureInfo.InvariantCulture);
422
423                         return CharSet.None.ToString (CultureInfo.InvariantCulture);
424                 }
425
426                 private static string GetLayout (Type type)
427                 {
428                         if (type.IsAutoLayout)
429                                 return LayoutKind.Auto.ToString (CultureInfo.InvariantCulture);
430
431                         if (type.IsExplicitLayout)
432                                 return LayoutKind.Explicit.ToString (CultureInfo.InvariantCulture);
433
434                         if (type.IsLayoutSequential)
435                                 return LayoutKind.Sequential.ToString (CultureInfo.InvariantCulture);
436
437                         return null;
438                 }
439
440                 private FieldInfo[] GetFields (Type type)
441                 {
442                         ArrayList list = new ArrayList ();
443
444                         FieldInfo[] fields = type.GetFields (flags);
445                         foreach (FieldInfo field in fields) {
446                                 if (field.IsSpecialName)
447                                         continue;
448
449                                 // we're only interested in public or protected members
450                                 if (!field.IsPublic && !field.IsFamily && !field.IsFamilyOrAssembly)
451                                         continue;
452
453                                 list.Add (field);
454                         }
455
456                         return (FieldInfo[]) list.ToArray (typeof (FieldInfo));
457                 }
458
459                 internal static PropertyInfo[] GetProperties (Type type)
460                 {
461                         ArrayList list = new ArrayList ();
462
463                         PropertyInfo[] properties = type.GetProperties (flags);
464                         foreach (PropertyInfo property in properties) {
465                                 MethodInfo getMethod = null;
466                                 MethodInfo setMethod = null;
467
468                                 if (property.CanRead) {
469                                         try { getMethod = property.GetGetMethod (true); }
470                                         catch (System.Security.SecurityException) { }
471                                 }
472                                 if (property.CanWrite) {
473                                         try { setMethod = property.GetSetMethod (true); }
474                                         catch (System.Security.SecurityException) { }
475                                 }
476
477                                 bool hasGetter = (getMethod != null) && MustDocumentMethod (getMethod);
478                                 bool hasSetter = (setMethod != null) && MustDocumentMethod (setMethod);
479
480                                 // if neither the getter or setter should be documented, then
481                                 // skip the property
482                                 if (!hasGetter && !hasSetter) {
483                                         continue;
484                                 }
485
486                                 list.Add (property);
487                         }
488
489                         return (PropertyInfo[]) list.ToArray (typeof (PropertyInfo));
490                 }
491
492                 private MethodInfo[] GetMethods (Type type)
493                 {
494                         ArrayList list = new ArrayList ();
495
496                         MethodInfo[] methods = type.GetMethods (flags);
497                         foreach (MethodInfo method in methods) {
498                                 if (method.IsSpecialName)
499                                         continue;
500
501                                 // we're only interested in public or protected members
502                                 if (!MustDocumentMethod(method))
503                                         continue;
504
505                                 list.Add (method);
506                         }
507
508                         return (MethodInfo[]) list.ToArray (typeof (MethodInfo));
509                 }
510
511                 private ConstructorInfo[] GetConstructors (Type type)
512                 {
513                         ArrayList list = new ArrayList ();
514
515                         ConstructorInfo[] ctors = type.GetConstructors (flags);
516                         foreach (ConstructorInfo constructor in ctors) {
517                                 // we're only interested in public or protected members
518                                 if (!constructor.IsPublic && !constructor.IsFamily && !constructor.IsFamilyOrAssembly)
519                                         continue;
520
521                                 list.Add (constructor);
522                         }
523
524                         return (ConstructorInfo[]) list.ToArray (typeof (ConstructorInfo));
525                 }
526
527                 private EventInfo[] GetEvents (Type type)
528                 {
529                         ArrayList list = new ArrayList ();
530
531                         EventInfo[] events = type.GetEvents (flags);
532                         foreach (EventInfo eventInfo in events) {
533                                 MethodInfo addMethod = eventInfo.GetAddMethod (true);
534
535                                 if (addMethod == null || !MustDocumentMethod (addMethod))
536                                         continue;
537
538                                 list.Add (eventInfo);
539                         }
540
541                         return (EventInfo[]) list.ToArray (typeof (EventInfo));
542                 }
543         }
544
545         class FieldData : MemberData
546         {
547                 public FieldData (XmlDocument document, XmlNode parent, FieldInfo [] members)
548                         : base (document, parent, members)
549                 {
550                 }
551
552                 protected override string GetName (MemberInfo member)
553                 {
554                         FieldInfo field = (FieldInfo) member;
555                         return field.Name;
556                 }
557
558                 protected override string GetMemberAttributes (MemberInfo member)
559                 {
560                         FieldInfo field = (FieldInfo) member;
561                         return ((int) field.Attributes).ToString (CultureInfo.InvariantCulture);
562                 }
563
564                 protected override void AddExtraData (XmlNode p, MemberInfo member)
565                 {
566                         base.AddExtraData (p, member);
567                         FieldInfo field = (FieldInfo) member;
568                         AddAttribute (p, "fieldtype", field.FieldType.ToString ());
569
570                         if (field.IsLiteral) {
571                                 object value = field.GetValue (null);
572                                 string stringValue = null;
573                                 if (value is Enum) {
574                                         // FIXME: when Mono bug #60090 has been
575                                         // fixed, we should just be able to use
576                                         // Convert.ToString
577                                         stringValue = ((Enum) value).ToString ("D", CultureInfo.InvariantCulture);
578                                 } else {
579                                         stringValue = Convert.ToString (value, CultureInfo.InvariantCulture);
580                                 }
581
582                                 if (stringValue != null)
583                                         AddAttribute (p, "value", stringValue);
584                         }
585                 }
586
587                 public override string ParentTag {
588                         get { return "fields"; }
589                 }
590
591                 public override string Tag {
592                         get { return "field"; }
593                 }
594         }
595
596         class PropertyData : MemberData
597         {
598                 public PropertyData (XmlDocument document, XmlNode parent, PropertyInfo [] members)
599                         : base (document, parent, members)
600                 {
601                 }
602
603                 protected override string GetName (MemberInfo member)
604                 {
605                         PropertyInfo prop = (PropertyInfo) member;
606                         return prop.Name;
607                 }
608
609                 protected override void AddExtraData (XmlNode p, MemberInfo member)
610                 {
611                         base.AddExtraData (p, member);
612                         PropertyInfo prop = (PropertyInfo) member;
613                         Type t = prop.PropertyType;
614                         AddAttribute (p, "ptype", prop.PropertyType.ToString ());
615                         MethodInfo _get = prop.GetGetMethod (true);
616                         MethodInfo _set = prop.GetSetMethod (true);
617                         bool haveGet = (_get != null && TypeData.MustDocumentMethod(_get));
618                         bool haveSet = (_set != null && TypeData.MustDocumentMethod(_set));
619                         MethodInfo [] methods;
620
621                         if (haveGet && haveSet) {
622                                 methods = new MethodInfo [] {_get, _set};
623                         } else if (haveGet) {
624                                 methods = new MethodInfo [] {_get};
625                         } else if (haveSet) {
626                                 methods = new MethodInfo [] {_set};
627                         } else {
628                                 //odd
629                                 return;
630                         }
631
632                         string parms = Parameters.GetSignature (methods [0].GetParameters ());
633                         AddAttribute (p, "params", parms);
634
635                         MethodData data = new MethodData (document, p, methods);
636                         //data.NoMemberAttributes = true;
637                         data.DoOutput ();
638                 }
639
640                 protected override string GetMemberAttributes (MemberInfo member)
641                 {
642                         PropertyInfo prop = (PropertyInfo) member;
643                         return ((int) prop.Attributes & (0xFFFFFFFF ^ (int) PropertyAttributes.ReservedMask)).ToString (CultureInfo.InvariantCulture);
644                 }
645
646                 public override string ParentTag {
647                         get { return "properties"; }
648                 }
649
650                 public override string Tag {
651                         get { return "property"; }
652                 }
653         }
654
655         class EventData : MemberData
656         {
657                 public EventData (XmlDocument document, XmlNode parent, EventInfo [] members)
658                         : base (document, parent, members)
659                 {
660                 }
661
662                 protected override string GetName (MemberInfo member)
663                 {
664                         EventInfo evt = (EventInfo) member;
665                         return evt.Name;
666                 }
667
668                 protected override string GetMemberAttributes (MemberInfo member)
669                 {
670                         EventInfo evt = (EventInfo) member;
671                         return ((int) evt.Attributes).ToString (CultureInfo.InvariantCulture);
672                 }
673
674                 protected override void AddExtraData (XmlNode p, MemberInfo member)
675                 {
676                         base.AddExtraData (p, member);
677                         EventInfo evt = (EventInfo) member;
678                         AddAttribute (p, "eventtype", evt.EventHandlerType.ToString ());
679                 }
680
681                 public override string ParentTag {
682                         get { return "events"; }
683                 }
684
685                 public override string Tag {
686                         get { return "event"; }
687                 }
688         }
689
690         class MethodData : MemberData
691         {
692                 bool noAtts;
693
694                 public MethodData (XmlDocument document, XmlNode parent, MethodBase [] members)
695                         : base (document, parent, members)
696                 {
697                 }
698
699                 protected override string GetName (MemberInfo member)
700                 {
701                         MethodBase method = (MethodBase) member;
702                         string name = method.Name;
703                         string parms = Parameters.GetSignature (method.GetParameters ());
704 #if NET_2_0
705                         MethodInfo mi = method as MethodInfo;
706                         Type [] genArgs = mi == null ? Type.EmptyTypes :
707                                 mi.GetGenericArguments ();
708                         if (genArgs.Length > 0) {
709                                 string [] genArgNames = new string [genArgs.Length];
710                                 for (int i = 0; i < genArgs.Length; i++) {
711                                         genArgNames [i] = genArgs [i].Name;
712                                         string genArgCsts = String.Empty;
713                                         Type [] gcs = genArgs [i].GetGenericParameterConstraints ();
714                                         if (gcs.Length > 0) {
715                                                 string [] gcNames = new string [gcs.Length];
716                                                 for (int g = 0; g < gcs.Length; g++)
717                                                         gcNames [g] = gcs [g].FullName;
718                                                 genArgCsts = String.Concat (
719                                                         "(",
720                                                         string.Join (", ", gcNames),
721                                                         ") ",
722                                                         genArgNames [i]);
723                                         }
724                                         else
725                                                 genArgCsts = genArgNames [i];
726                                         if ((genArgs [i].GenericParameterAttributes & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
727                                                 genArgCsts = "class " + genArgCsts;
728                                         else if ((genArgs [i].GenericParameterAttributes & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
729                                                 genArgCsts = "struct " + genArgCsts;
730                                         genArgNames [i] = genArgCsts;
731                                 }
732                                 return String.Format ("{0}<{2}>({1})",
733                                         name,
734                                         parms,
735                                         string.Join (",", genArgNames));
736                         }
737 #endif
738                         return String.Format ("{0}({1})", name, parms);
739                 }
740
741                 protected override string GetMemberAttributes (MemberInfo member)
742                 {
743                         MethodBase method = (MethodBase) member;
744                         return ((int)( method.Attributes & ~MethodAttributes.ReservedMask)).ToString (CultureInfo.InvariantCulture);
745                 }
746
747                 protected override void AddExtraData (XmlNode p, MemberInfo member)
748                 {
749                         base.AddExtraData (p, member);
750
751                         ParameterData parms = new ParameterData (document, p, 
752                                 ((MethodBase) member).GetParameters ());
753                         parms.DoOutput ();
754
755                         if (!(member is MethodBase))
756                                 return;
757
758                         MethodBase mbase = (MethodBase) member;
759
760                         if (mbase.IsAbstract)
761                                 AddAttribute (p, "abstract", "true");
762                         if (mbase.IsVirtual)
763                                 AddAttribute (p, "virtual", "true");
764                         if (mbase.IsStatic)
765                                 AddAttribute (p, "static", "true");
766
767                         if (!(member is MethodInfo))
768                                 return;
769
770                         MethodInfo method = (MethodInfo) member;
771                         AddAttribute (p, "returntype", method.ReturnType.ToString ());
772
773                         AttributeData.OutputAttributes (document, p,
774                                 method.ReturnTypeCustomAttributes.GetCustomAttributes (false));
775 #if NET_2_0
776                         // Generic constraints
777                         Type [] gargs = method.GetGenericArguments ();
778                         XmlElement ngeneric = (gargs.Length == 0) ? null :
779                                 document.CreateElement ("generic-method-constraints");
780                         foreach (Type garg in gargs) {
781                                 Type [] csts = garg.GetGenericParameterConstraints ();
782                                 if (csts.Length == 0 || csts [0] == typeof (object))
783                                         continue;
784                                 XmlElement el = document.CreateElement ("generic-method-constraint");
785                                 el.SetAttribute ("name", garg.ToString ());
786                                 el.SetAttribute ("generic-attribute",
787                                         garg.GenericParameterAttributes.ToString ());
788                                 ngeneric.AppendChild (el);
789                                 foreach (Type ct in csts) {
790                                         XmlElement cel = document.CreateElement ("type");
791                                         cel.AppendChild (document.CreateTextNode (ct.FullName));
792                                         el.AppendChild (cel);
793                                 }
794                         }
795                         if (ngeneric != null && ngeneric.FirstChild != null)
796                                 p.AppendChild (ngeneric);
797 #endif
798
799                 }
800
801                 public override bool NoMemberAttributes {
802                         get { return noAtts; }
803                         set { noAtts = value; }
804                 }
805                 
806                 public override string ParentTag {
807                         get { return "methods"; }
808                 }
809
810                 public override string Tag {
811                         get { return "method"; }
812                 }
813         }
814
815         class ConstructorData : MethodData
816         {
817                 public ConstructorData (XmlDocument document, XmlNode parent, ConstructorInfo [] members)
818                         : base (document, parent, members)
819                 {
820                 }
821
822                 public override string ParentTag {
823                         get { return "constructors"; }
824                 }
825
826                 public override string Tag {
827                         get { return "constructor"; }
828                 }
829         }
830
831         class ParameterData : BaseData
832         {
833                 private ParameterInfo[] parameters;
834
835                 public ParameterData (XmlDocument document, XmlNode parent, ParameterInfo[] parameters)
836                         : base (document, parent)
837                 {
838                         this.parameters = parameters;
839                 }
840
841                 public override void DoOutput ()
842                 {
843                         XmlNode parametersNode = document.CreateElement ("parameters", null);
844                         parent.AppendChild (parametersNode);
845
846                         foreach (ParameterInfo parameter in parameters) {
847                                 XmlNode paramNode = document.CreateElement ("parameter", null);
848                                 parametersNode.AppendChild (paramNode);
849                                 AddAttribute (paramNode, "name", parameter.Name);
850                                 AddAttribute (paramNode, "position", parameter.Position.ToString(CultureInfo.InvariantCulture));
851                                 AddAttribute (paramNode, "attrib", ((int) parameter.Attributes).ToString());
852
853                                 string direction = "in";
854
855                                 if (parameter.ParameterType.IsByRef) {
856                                         direction = parameter.IsOut ? "out" : "ref";
857                                 }
858
859                                 Type t = parameter.ParameterType;
860                                 AddAttribute (paramNode, "type", t.ToString ());
861
862                                 if (parameter.IsOptional) {
863                                         AddAttribute (paramNode, "optional", "true");
864                                         if (parameter.DefaultValue != System.DBNull.Value)
865                                                 AddAttribute (paramNode, "defaultValue", (parameter.DefaultValue == null) ? "NULL" : parameter.DefaultValue.ToString ());
866                                 }
867
868                                 if (direction != "in")
869                                         AddAttribute (paramNode, "direction", direction);
870
871                                 AttributeData.OutputAttributes (document, paramNode, parameter.GetCustomAttributes (false));
872                         }
873                 }
874         }
875
876         class AttributeData : BaseData
877         {
878                 object [] atts;
879
880                 AttributeData (XmlDocument doc, XmlNode parent, object[] attributes)
881                         : base (doc, parent)
882                 {
883                         atts = attributes;
884                 }
885
886                 public override void DoOutput ()
887                 {
888                         if (document == null)
889                                 throw new InvalidOperationException ("Document not set");
890
891                         if (atts == null || atts.Length == 0)
892                                 return;
893
894                         XmlNode natts = parent.SelectSingleNode("attributes");
895                         if (natts == null) {
896                                 natts = document.CreateElement ("attributes", null);
897                                 parent.AppendChild (natts);
898                         }
899
900                         for (int i = 0; i < atts.Length; ++i) {
901                                 Type t = atts [i].GetType ();
902                                 if (!t.IsPublic && !t.Name.EndsWith ("TODOAttribute"))
903                                         continue;
904
905                                 // we ignore attributes that inherit from SecurityAttribute on purpose as they:
906                                 // * aren't part of GetCustomAttributes in Fx 1.0/1.1;
907                                 // * are encoded differently and in a different metadata table; and
908                                 // * won't ever exactly match MS implementation (from a syntax pov)
909                                 if (t.IsSubclassOf (typeof (SecurityAttribute)))
910                                         continue;
911
912                                 XmlNode node = document.CreateElement ("attribute");
913                                 AddAttribute (node, "name", t.ToString ());
914
915                                 XmlNode properties = null;
916                                 foreach (PropertyInfo pi in TypeData.GetProperties (t)) {
917                                         if (pi.Name == "TypeId")
918                                                 continue;
919
920                                         if (properties == null) {
921                                                 properties = node.AppendChild (document.CreateElement ("properties"));
922                                         }
923
924                                         try {
925                                                 object o = pi.GetValue (atts [i], null);
926
927                                                 XmlNode n = properties.AppendChild (document.CreateElement ("property"));
928                                                 AddAttribute (n, "name", pi.Name);
929
930                                                 if (o == null) {
931                                                         AddAttribute (n, "null", "true");
932                                                         continue;
933                                                 }
934
935                                                 string value = o.ToString ();
936                                                 if (t == typeof (GuidAttribute))
937                                                         value = value.ToUpper ();
938
939                                                 AddAttribute (n, "value", value);
940                                         }
941                                         catch (TargetInvocationException) {
942                                                 continue;
943                                         }
944                                 }
945
946                                 natts.AppendChild (node);
947                         }
948                 }
949
950                 public static void OutputAttributes (XmlDocument doc, XmlNode parent, object[] attributes)
951                 {
952                         AttributeData ad = new AttributeData (doc, parent, attributes);
953                         ad.DoOutput ();
954                 }
955
956                 private static bool MustDocumentAttribute (Type attributeType)
957                 {
958                         // only document MonoTODOAttribute and public attributes
959                         return attributeType.Name.EndsWith ("TODOAttribute") || attributeType.IsPublic;
960                 }
961         }
962
963         class Parameters
964         {
965                 private Parameters () {}
966
967                 public static string GetSignature (ParameterInfo [] infos)
968                 {
969                         if (infos == null || infos.Length == 0)
970                                 return "";
971
972                         StringBuilder sb = new StringBuilder ();
973                         foreach (ParameterInfo info in infos) {
974                                 string modifier;
975                                 if (info.IsIn)
976                                         modifier = "in ";
977                                 else if (info.IsRetval)
978                                         modifier = "ref ";
979                                 else if (info.IsOut)
980                                         modifier = "out ";
981                                 else
982                                         modifier = "";
983
984                                 string type_name = info.ParameterType.ToString ().Replace ('<', '[').Replace ('>', ']');
985                                 sb.AppendFormat ("{0}{1}, ", modifier, type_name);
986                         }
987
988                         sb.Length -= 2; // remove ", "
989                         return sb.ToString ();
990                 }
991
992         }
993         
994         class TypeComparer : IComparer
995         {
996                 public static TypeComparer Default = new TypeComparer ();
997
998                 public int Compare (object a, object b)
999                 {
1000                         Type ta = (Type) a;
1001                         Type tb = (Type) b;
1002                         int result = String.Compare (ta.Namespace, tb.Namespace);
1003                         if (result != 0)
1004                                 return result;
1005
1006                         return String.Compare (ta.Name, tb.Name);
1007                 }
1008         }
1009
1010         class MemberInfoComparer : IComparer
1011         {
1012                 public static MemberInfoComparer Default = new MemberInfoComparer ();
1013
1014                 public int Compare (object a, object b)
1015                 {
1016                         MemberInfo ma = (MemberInfo) a;
1017                         MemberInfo mb = (MemberInfo) b;
1018                         return String.Compare (ma.Name, mb.Name);
1019                 }
1020         }
1021
1022         class MethodBaseComparer : IComparer
1023         {
1024                 public static MethodBaseComparer Default = new MethodBaseComparer ();
1025
1026                 public int Compare (object a, object b)
1027                 {
1028                         MethodBase ma = (MethodBase) a;
1029                         MethodBase mb = (MethodBase) b;
1030                         int res = String.Compare (ma.Name, mb.Name);
1031                         if (res != 0)
1032                                 return res;
1033
1034                         ParameterInfo [] pia = ma.GetParameters ();
1035                         ParameterInfo [] pib = mb.GetParameters ();
1036                         if (pia.Length != pib.Length)
1037                                 return pia.Length - pib.Length;
1038
1039                         string siga = Parameters.GetSignature (pia);
1040                         string sigb = Parameters.GetSignature (pib);
1041                         return String.Compare (siga, sigb);
1042                 }
1043         }
1044 }
1045