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