2 // mono-api-info.cs - Dumps public assembly information to an xml file.
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // (C) 2003 Novell, Inc (http://www.novell.com)
11 using System.Collections;
12 using System.Globalization;
13 using System.Reflection;
14 using System.Runtime.InteropServices;
18 namespace Mono.AssemblyInfo
22 static int Main (string [] args)
27 AssemblyCollection acoll = new AssemblyCollection ();
29 foreach (string fullName in args) {
33 XmlDocument doc = new XmlDocument ();
37 XmlTextWriter writer = new XmlTextWriter (Console.Out);
38 writer.Formatting = Formatting.Indented;
39 XmlNode decl = doc.CreateXmlDeclaration ("1.0", null, null);
40 doc.InsertBefore (decl, doc.DocumentElement);
46 class AssemblyCollection
51 public AssemblyCollection ()
53 assemblies = new ArrayList ();
56 public bool Add (string name)
58 Assembly ass = LoadAssembly (name);
66 public void DoOutput ()
69 throw new InvalidOperationException ("Document not set");
71 XmlNode nassemblies = document.CreateElement ("assemblies", null);
72 document.AppendChild (nassemblies);
73 foreach (Assembly a in assemblies) {
74 AssemblyData data = new AssemblyData (document, nassemblies, a);
79 public XmlDocument Document {
80 set { document = value; }
83 static Assembly LoadAssembly (string aname)
88 if (!name.EndsWith (".dll"))
90 ass = Assembly.LoadFrom (name);
95 ass = Assembly.LoadWithPartialName (aname);
103 abstract class BaseData
105 protected XmlDocument document;
106 protected XmlNode parent;
108 protected BaseData (XmlDocument doc, XmlNode parent)
111 this.parent = parent;
114 public abstract void DoOutput ();
116 protected void AddAttribute (XmlNode node, string name, string value)
118 XmlAttribute attr = document.CreateAttribute (name);
120 node.Attributes.Append (attr);
124 class AssemblyData : BaseData
128 public AssemblyData (XmlDocument document, XmlNode parent, Assembly ass)
129 : base (document, parent)
134 public override void DoOutput ()
136 if (document == null)
137 throw new InvalidOperationException ("Document not set");
139 XmlNode nassembly = document.CreateElement ("assembly", null);
140 AssemblyName aname = ass.GetName ();
141 AddAttribute (nassembly, "name", aname.Name);
142 AddAttribute (nassembly, "version", aname.Version.ToString ());
143 parent.AppendChild (nassembly);
144 AttributeData.OutputAttributes (document, nassembly, ass.GetCustomAttributes (false));
145 Type [] types = ass.GetTypes ();
146 if (types == null || types.Length == 0)
149 Array.Sort (types, TypeComparer.Default);
151 XmlNode nss = document.CreateElement ("namespaces", null);
152 nassembly.AppendChild (nss);
154 string currentNS = "$%&$&";
156 XmlNode classes = null;
157 foreach (Type t in types) {
158 if (t.Namespace == null || t.Namespace == "")
164 if (t.IsNestedPublic || t.IsNestedAssembly || t.IsNestedFamANDAssem ||
165 t.IsNestedFamORAssem || t.IsNestedPrivate)
168 if (t.DeclaringType != null)
169 continue; // enforce !nested
171 if (t.Namespace != currentNS) {
172 currentNS = t.Namespace;
173 ns = document.CreateElement ("namespace", null);
174 AddAttribute (ns, "name", currentNS);
175 nss.AppendChild (ns);
176 classes = document.CreateElement ("classes", null);
177 ns.AppendChild (classes);
180 TypeData bd = new TypeData (document, classes, t);
186 abstract class MemberData : BaseData
188 MemberInfo [] members;
190 public MemberData (XmlDocument document, XmlNode parent, MemberInfo [] members)
191 : base (document, parent)
193 this.members = members;
196 public override void DoOutput ()
198 XmlNode mclass = document.CreateElement (ParentTag, null);
199 parent.AppendChild (mclass);
201 foreach (MemberInfo member in members) {
202 XmlNode mnode = document.CreateElement (Tag, null);
203 mclass.AppendChild (mnode);
204 AddAttribute (mnode, "name", GetName (member));
205 if (!NoMemberAttributes)
206 AddAttribute (mnode, "attrib", GetMemberAttributes (member));
208 AttributeData.OutputAttributes (document, mnode,
209 member.GetCustomAttributes (false));
211 AddExtraData (mnode, member);
215 protected virtual void AddExtraData (XmlNode p, MemberInfo member)
219 protected virtual string GetName (MemberInfo member)
224 protected virtual string GetMemberAttributes (MemberInfo member)
229 public virtual bool NoMemberAttributes {
230 get { return false; }
234 public virtual string ParentTag {
235 get { return "NoPARENTTAG"; }
238 public virtual string Tag {
239 get { return "NoTAG"; }
243 class TypeData : MemberData
246 const BindingFlags flags = BindingFlags.Public | BindingFlags.Static |
247 BindingFlags.Instance | BindingFlags.DeclaredOnly |
248 BindingFlags.NonPublic;
250 public TypeData (XmlDocument document, XmlNode parent, Type type)
251 : base (document, parent, null)
256 public override void DoOutput ()
258 if (document == null)
259 throw new InvalidOperationException ("Document not set");
261 XmlNode nclass = document.CreateElement ("class", null);
262 AddAttribute (nclass, "name", type.Name);
263 string classType = GetClassType (type);
264 AddAttribute (nclass, "type", classType);
266 if (type.BaseType != null)
267 AddAttribute (nclass, "base", type.BaseType.FullName);
270 AddAttribute (nclass, "sealed", "true");
272 if (type.IsSerializable)
273 AddAttribute (nclass, "serializable", "true");
275 string charSet = GetCharSet (type);
276 AddAttribute (nclass, "charset", charSet);
278 string layout = GetLayout (type);
280 AddAttribute (nclass, "layout", layout);
282 parent.AppendChild (nclass);
284 AttributeData.OutputAttributes (document, nclass, type.GetCustomAttributes (false));
286 Type [] interfaces = type.GetInterfaces ();
287 if (interfaces != null && interfaces.Length > 0) {
288 XmlNode ifaces = document.CreateElement ("interfaces", null);
289 nclass.AppendChild (ifaces);
290 foreach (Type t in interfaces) {
292 // we're only interested in public interfaces
295 XmlNode iface = document.CreateElement ("interface", null);
296 AddAttribute (iface, "name", t.FullName);
297 ifaces.AppendChild (iface);
301 ArrayList members = new ArrayList ();
303 FieldInfo[] fields = GetFields (type);
304 if (fields.Length > 0) {
305 Array.Sort (fields, MemberInfoComparer.Default);
306 FieldData fd = new FieldData (document, nclass, fields);
307 // Special case for enum fields
308 if (classType == "enum") {
309 string etype = fields [0].GetType ().FullName;
310 AddAttribute (nclass, "enumtype", etype);
315 ConstructorInfo [] ctors = GetConstructors (type);
316 if (ctors.Length > 0) {
317 Array.Sort (ctors, MemberInfoComparer.Default);
318 members.Add (new ConstructorData (document, nclass, ctors));
321 PropertyInfo[] properties = GetProperties (type);
322 if (properties.Length > 0) {
323 Array.Sort (properties, MemberInfoComparer.Default);
324 members.Add (new PropertyData (document, nclass, properties));
327 EventInfo [] events = GetEvents (type);
328 if (events.Length > 0) {
329 Array.Sort (events, MemberInfoComparer.Default);
330 members.Add (new EventData (document, nclass, events));
333 MethodInfo [] methods = GetMethods (type);
334 if (methods.Length > 0) {
335 Array.Sort (methods, MemberInfoComparer.Default);
336 members.Add (new MethodData (document, nclass, methods));
339 foreach (MemberData md in members)
342 Type [] nested = type.GetNestedTypes ();
343 if (nested != null && nested.Length > 0) {
344 XmlNode classes = document.CreateElement ("classes", null);
345 nclass.AppendChild (classes);
346 foreach (Type t in nested) {
347 TypeData td = new TypeData (document, classes, t);
353 protected override string GetMemberAttributes (MemberInfo member)
356 throw new InvalidOperationException ("odd");
358 return ((int) type.Attributes).ToString (CultureInfo.InvariantCulture);
361 public static bool MustDocumentMethod(MethodBase method)
364 return (method.IsPublic || method.IsFamily || method.IsFamilyOrAssembly);
367 static string GetClassType (Type t)
378 if (typeof (Delegate).IsAssignableFrom (t))
384 private static string GetCharSet (Type type)
386 if (type.IsAnsiClass)
387 return CharSet.Ansi.ToString (CultureInfo.InvariantCulture);
389 if (type.IsAutoClass)
390 return CharSet.Auto.ToString (CultureInfo.InvariantCulture);
392 if (type.IsUnicodeClass)
393 return CharSet.Unicode.ToString (CultureInfo.InvariantCulture);
395 return CharSet.None.ToString (CultureInfo.InvariantCulture);
398 private static string GetLayout (Type type)
400 if (type.IsAutoLayout)
401 return LayoutKind.Auto.ToString (CultureInfo.InvariantCulture);
403 if (type.IsExplicitLayout)
404 return LayoutKind.Explicit.ToString (CultureInfo.InvariantCulture);
406 if (type.IsLayoutSequential)
407 return LayoutKind.Sequential.ToString (CultureInfo.InvariantCulture);
412 private FieldInfo[] GetFields (Type type)
414 ArrayList list = new ArrayList ();
416 FieldInfo[] fields = type.GetFields (flags);
417 foreach (FieldInfo field in fields) {
418 if (field.IsSpecialName)
421 // we're only interested in public or protected members
422 if (!field.IsPublic && !field.IsFamily && !field.IsFamilyOrAssembly)
428 return (FieldInfo[]) list.ToArray (typeof (FieldInfo));
431 private PropertyInfo[] GetProperties (Type type)
433 ArrayList list = new ArrayList ();
435 PropertyInfo[] properties = type.GetProperties (flags);
436 foreach (PropertyInfo property in properties) {
437 MethodInfo getMethod = null;
438 MethodInfo setMethod = null;
440 if (property.CanRead) {
441 try { getMethod = property.GetGetMethod (true); }
442 catch (System.Security.SecurityException) { }
444 if (property.CanWrite) {
445 try { setMethod = property.GetSetMethod (true); }
446 catch (System.Security.SecurityException) { }
449 bool hasGetter = (getMethod != null) && MustDocumentMethod (getMethod);
450 bool hasSetter = (setMethod != null) && MustDocumentMethod (setMethod);
452 // if neither the getter or setter should be documented, then
454 if (!hasGetter && !hasSetter) {
461 return (PropertyInfo[]) list.ToArray (typeof (PropertyInfo));
464 private MethodInfo[] GetMethods (Type type)
466 ArrayList list = new ArrayList ();
468 MethodInfo[] methods = type.GetMethods (flags);
469 foreach (MethodInfo method in methods) {
470 if (method.IsSpecialName)
473 // we're only interested in public or protected members
474 if (!MustDocumentMethod(method))
480 return (MethodInfo[]) list.ToArray (typeof (MethodInfo));
483 private ConstructorInfo[] GetConstructors (Type type)
485 ArrayList list = new ArrayList ();
487 ConstructorInfo[] ctors = type.GetConstructors (flags);
488 foreach (ConstructorInfo constructor in ctors) {
489 // we're only interested in public or protected members
490 if (!constructor.IsPublic && !constructor.IsFamily && !constructor.IsFamilyOrAssembly)
493 list.Add (constructor);
496 return (ConstructorInfo[]) list.ToArray (typeof (ConstructorInfo));
499 private EventInfo[] GetEvents (Type type)
501 ArrayList list = new ArrayList ();
503 EventInfo[] events = type.GetEvents (flags);
504 foreach (EventInfo eventInfo in events) {
505 MethodInfo addMethod = eventInfo.GetAddMethod (true);
507 if (addMethod == null || !MustDocumentMethod (addMethod))
510 list.Add (eventInfo);
513 return (EventInfo[]) list.ToArray (typeof (EventInfo));
517 class FieldData : MemberData
519 public FieldData (XmlDocument document, XmlNode parent, FieldInfo [] members)
520 : base (document, parent, members)
524 protected override string GetName (MemberInfo member)
526 FieldInfo field = (FieldInfo) member;
530 protected override string GetMemberAttributes (MemberInfo member)
532 FieldInfo field = (FieldInfo) member;
533 return ((int) field.Attributes).ToString (CultureInfo.InvariantCulture);
536 protected override void AddExtraData (XmlNode p, MemberInfo member)
538 base.AddExtraData (p, member);
539 FieldInfo field = (FieldInfo) member;
540 AddAttribute (p, "fieldtype", field.FieldType.FullName);
542 if (field.IsLiteral) {
543 object value = field.GetValue (null);
544 string stringValue = null;
546 // FIXME: when Mono bug #60090 has been
547 // fixed, we should just be able to use
549 stringValue = ((Enum) value).ToString ("D", CultureInfo.InvariantCulture);
551 stringValue = Convert.ToString (value, CultureInfo.InvariantCulture);
554 if (stringValue != null)
555 AddAttribute (p, "value", stringValue);
559 public override string ParentTag {
560 get { return "fields"; }
563 public override string Tag {
564 get { return "field"; }
568 class PropertyData : MemberData
570 public PropertyData (XmlDocument document, XmlNode parent, PropertyInfo [] members)
571 : base (document, parent, members)
575 protected override string GetName (MemberInfo member)
577 PropertyInfo prop = (PropertyInfo) member;
581 protected override void AddExtraData (XmlNode p, MemberInfo member)
583 base.AddExtraData (p, member);
584 PropertyInfo prop = (PropertyInfo) member;
585 AddAttribute (p, "ptype", prop.PropertyType.FullName);
586 MethodInfo _get = prop.GetGetMethod (true);
587 MethodInfo _set = prop.GetSetMethod (true);
588 bool haveGet = (_get != null && TypeData.MustDocumentMethod(_get));
589 bool haveSet = (_set != null && TypeData.MustDocumentMethod(_set));
590 MethodInfo [] methods;
592 if (haveGet && haveSet) {
593 methods = new MethodInfo [] {_get, _set};
594 } else if (haveGet) {
595 methods = new MethodInfo [] {_get};
596 } else if (haveSet) {
597 methods = new MethodInfo [] {_set};
603 string parms = Parameters.GetSignature (methods [0].GetParameters ());
604 AddAttribute (p, "params", parms);
606 MethodData data = new MethodData (document, p, methods);
607 data.NoMemberAttributes = true;
611 protected override string GetMemberAttributes (MemberInfo member)
613 PropertyInfo prop = (PropertyInfo) member;
614 return ((int) prop.Attributes).ToString (CultureInfo.InvariantCulture);
617 public override string ParentTag {
618 get { return "properties"; }
621 public override string Tag {
622 get { return "property"; }
626 class EventData : MemberData
628 public EventData (XmlDocument document, XmlNode parent, EventInfo [] members)
629 : base (document, parent, members)
633 protected override string GetName (MemberInfo member)
635 EventInfo evt = (EventInfo) member;
639 protected override string GetMemberAttributes (MemberInfo member)
641 EventInfo evt = (EventInfo) member;
642 return ((int) evt.Attributes).ToString (CultureInfo.InvariantCulture);
645 protected override void AddExtraData (XmlNode p, MemberInfo member)
647 base.AddExtraData (p, member);
648 EventInfo evt = (EventInfo) member;
649 AddAttribute (p, "eventtype", evt.EventHandlerType.FullName);
652 public override string ParentTag {
653 get { return "events"; }
656 public override string Tag {
657 get { return "event"; }
661 class MethodData : MemberData
665 public MethodData (XmlDocument document, XmlNode parent, MethodBase [] members)
666 : base (document, parent, members)
670 protected override string GetName (MemberInfo member)
672 MethodBase method = (MethodBase) member;
673 string name = method.Name;
674 string parms = Parameters.GetSignature (method.GetParameters ());
675 return String.Format ("{0}({1})", name, parms);
678 protected override string GetMemberAttributes (MemberInfo member)
680 MethodBase method = (MethodBase) member;
681 return ((int) method.Attributes).ToString (CultureInfo.InvariantCulture);
684 protected override void AddExtraData (XmlNode p, MemberInfo member)
686 base.AddExtraData (p, member);
688 ParameterData parms = new ParameterData (document, p,
689 ((MethodBase) member).GetParameters ());
692 if (!(member is MethodInfo))
695 MethodInfo method = (MethodInfo) member;
696 AddAttribute (p, "returntype", method.ReturnType.FullName);
698 AttributeData.OutputAttributes (document, p,
699 method.ReturnTypeCustomAttributes.GetCustomAttributes (false));
702 public override bool NoMemberAttributes {
703 get { return noAtts; }
704 set { noAtts = value; }
707 public override string ParentTag {
708 get { return "methods"; }
711 public override string Tag {
712 get { return "method"; }
716 class ConstructorData : MethodData
718 public ConstructorData (XmlDocument document, XmlNode parent, ConstructorInfo [] members)
719 : base (document, parent, members)
723 public override string ParentTag {
724 get { return "constructors"; }
727 public override string Tag {
728 get { return "constructor"; }
732 class ParameterData : BaseData
734 private ParameterInfo[] parameters;
736 public ParameterData (XmlDocument document, XmlNode parent, ParameterInfo[] parameters)
737 : base (document, parent)
739 this.parameters = parameters;
742 public override void DoOutput ()
744 XmlNode parametersNode = document.CreateElement ("parameters", null);
745 parent.AppendChild (parametersNode);
747 foreach (ParameterInfo parameter in parameters) {
748 XmlNode paramNode = document.CreateElement ("parameter", null);
749 parametersNode.AppendChild (paramNode);
750 AddAttribute (paramNode, "name", parameter.Name);
751 AddAttribute (paramNode, "position", parameter.Position.ToString(CultureInfo.InvariantCulture));
752 AddAttribute (paramNode, "attrib", ((int) parameter.Attributes).ToString());
754 string direction = "in";
756 if (parameter.ParameterType.IsByRef) {
757 direction = parameter.IsOut ? "out" : "ref";
760 Type t = parameter.ParameterType;
761 AddAttribute (paramNode, "type", t.FullName);
763 if (parameter.IsOptional) {
764 AddAttribute (paramNode, "optional", "true");
765 if (parameter.DefaultValue != null)
766 AddAttribute (paramNode, "defaultValue", parameter.DefaultValue.ToString ());
769 if (direction != "in")
770 AddAttribute (paramNode, "direction", direction);
772 AttributeData.OutputAttributes (document, paramNode, parameter.GetCustomAttributes (false));
777 class AttributeData : BaseData
782 AttributeData (XmlDocument doc, XmlNode parent, object[] attributes, string target)
786 this.target = target;
789 AttributeData (XmlDocument doc, XmlNode parent, object [] attributes)
790 : this (doc, parent, attributes, null)
794 public override void DoOutput ()
796 if (document == null)
797 throw new InvalidOperationException ("Document not set");
799 if (atts == null || atts.Length == 0)
802 XmlNode natts = parent.SelectSingleNode("attributes");
804 natts = document.CreateElement ("attributes", null);
805 parent.AppendChild (natts);
808 ArrayList typeList = new ArrayList (atts.Length);
809 string comment = null;
810 for (int i = atts.Length - 1; i >= 0; i--) {
811 Type attType = atts [i].GetType ();
812 if (!MustDocumentAttribute (attType))
814 typeList.Add (attType);
815 if (attType.Name.EndsWith ("TODOAttribute")) {
816 PropertyInfo prop = attType.GetProperty ("Comment");
818 comment = (string) prop.GetValue (atts [i], null);
822 Type[] types = (Type[]) typeList.ToArray (typeof (Type));
823 Array.Sort (types, TypeComparer.Default);
824 foreach (Type t in types) {
825 XmlNode node = document.CreateElement ("attribute");
826 AddAttribute (node, "name", t.FullName);
827 if (target != null) {
828 AddAttribute (node, "target", target);
830 if (comment != null && t.Name.EndsWith ("TODOAttribute"))
831 AddAttribute (node, "comment", comment);
833 natts.AppendChild (node);
837 public static void OutputAttributes (XmlDocument doc, XmlNode parent, object[] attributes)
839 AttributeData ad = new AttributeData (doc, parent, attributes, null);
843 public static void OutputAttributes (XmlDocument doc, XmlNode parent, object [] attributes, string target)
845 AttributeData ad = new AttributeData (doc, parent, attributes, target);
849 private static bool MustDocumentAttribute (Type attributeType)
851 // only document MonoTODOAttribute and public attributes
852 return attributeType.Name.EndsWith ("TODOAttribute") || attributeType.IsPublic;
858 private Parameters () {}
860 public static string GetSignature (ParameterInfo [] infos)
862 if (infos == null || infos.Length == 0)
865 StringBuilder sb = new StringBuilder ();
866 foreach (ParameterInfo info in infos) {
870 else if (info.IsRetval)
877 string type_name = info.ParameterType.ToString ();
878 sb.AppendFormat ("{0}{1}, ", modifier, type_name);
881 sb.Length -= 2; // remove ", "
882 return sb.ToString ();
887 class TypeComparer : IComparer
889 public static TypeComparer Default = new TypeComparer ();
891 public int Compare (object a, object b)
895 int result = String.Compare (ta.Namespace, tb.Namespace);
899 return String.Compare (ta.Name, tb.Name);
903 class MemberInfoComparer : IComparer
905 public static MemberInfoComparer Default = new MemberInfoComparer ();
907 public int Compare (object a, object b)
909 MemberInfo ma = (MemberInfo) a;
910 MemberInfo mb = (MemberInfo) b;
911 return String.Compare (ma.Name, mb.Name);
915 class MethodBaseComparer : IComparer
917 public static MethodBaseComparer Default = new MethodBaseComparer ();
919 public int Compare (object a, object b)
921 MethodBase ma = (MethodBase) a;
922 MethodBase mb = (MethodBase) b;
923 int res = String.Compare (ma.Name, mb.Name);
927 ParameterInfo [] pia = ma.GetParameters ();
928 ParameterInfo [] pib = mb.GetParameters ();
929 if (pia.Length != pib.Length)
930 return pia.Length - pib.Length;
932 string siga = Parameters.GetSignature (pia);
933 string sigb = Parameters.GetSignature (pib);
934 return String.Compare (siga, sigb);