2 // mono-api-info.cs - Dumps public assembly information to an xml file.
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // Copyright (C) 2003-2005 Novell, Inc (http://www.novell.com)
11 using System.Collections;
12 using System.Globalization;
13 using System.Reflection;
14 using System.Runtime.InteropServices;
15 using System.Security.Permissions;
19 namespace Mono.AssemblyInfo
23 static int Main (string [] args)
28 AssemblyCollection acoll = new AssemblyCollection ();
30 foreach (string fullName in args) {
34 XmlDocument doc = new XmlDocument ();
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);
47 class AssemblyCollection
52 public AssemblyCollection ()
54 assemblies = new ArrayList ();
57 public bool Add (string name)
59 Assembly ass = LoadAssembly (name);
67 public void DoOutput ()
70 throw new InvalidOperationException ("Document not set");
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);
80 public XmlDocument Document {
81 set { document = value; }
84 static Assembly LoadAssembly (string aname)
89 if (!name.EndsWith (".dll"))
91 ass = Assembly.LoadFrom (name);
96 ass = Assembly.LoadWithPartialName (aname);
104 abstract class BaseData
106 protected XmlDocument document;
107 protected XmlNode parent;
109 protected BaseData (XmlDocument doc, XmlNode parent)
112 this.parent = parent;
115 public abstract void DoOutput ();
117 protected void AddAttribute (XmlNode node, string name, string value)
119 XmlAttribute attr = document.CreateAttribute (name);
121 node.Attributes.Append (attr);
125 class AssemblyData : BaseData
129 public AssemblyData (XmlDocument document, XmlNode parent, Assembly ass)
130 : base (document, parent)
135 public override void DoOutput ()
137 if (document == null)
138 throw new InvalidOperationException ("Document not set");
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)
150 Array.Sort (types, TypeComparer.Default);
152 XmlNode nss = document.CreateElement ("namespaces", null);
153 nassembly.AppendChild (nss);
155 string currentNS = "$%&$&";
157 XmlNode classes = null;
158 foreach (Type t in types) {
159 if (t.Namespace == null || t.Namespace == "")
165 if (t.IsNestedPublic || t.IsNestedAssembly || t.IsNestedFamANDAssem ||
166 t.IsNestedFamORAssem || t.IsNestedPrivate)
169 if (t.DeclaringType != null)
170 continue; // enforce !nested
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);
181 TypeData bd = new TypeData (document, classes, t);
187 abstract class MemberData : BaseData
189 MemberInfo [] members;
191 public MemberData (XmlDocument document, XmlNode parent, MemberInfo [] members)
192 : base (document, parent)
194 this.members = members;
197 public override void DoOutput ()
199 XmlNode mclass = document.CreateElement (ParentTag, null);
200 parent.AppendChild (mclass);
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));
209 AttributeData.OutputAttributes (document, mnode,
210 member.GetCustomAttributes (false));
212 AddExtraData (mnode, member);
216 protected virtual void AddExtraData (XmlNode p, MemberInfo member)
220 protected virtual string GetName (MemberInfo member)
225 protected virtual string GetMemberAttributes (MemberInfo member)
230 public virtual bool NoMemberAttributes {
231 get { return false; }
235 public virtual string ParentTag {
236 get { return "NoPARENTTAG"; }
239 public virtual string Tag {
240 get { return "NoTAG"; }
244 class TypeData : MemberData
247 const BindingFlags flags = BindingFlags.Public | BindingFlags.Static |
248 BindingFlags.Instance | BindingFlags.DeclaredOnly |
249 BindingFlags.NonPublic;
251 public TypeData (XmlDocument document, XmlNode parent, Type type)
252 : base (document, parent, null)
257 public override void DoOutput ()
259 if (document == null)
260 throw new InvalidOperationException ("Document not set");
262 XmlNode nclass = document.CreateElement ("class", null);
263 AddAttribute (nclass, "name", type.Name);
264 string classType = GetClassType (type);
265 AddAttribute (nclass, "type", classType);
267 if (type.BaseType != null)
268 AddAttribute (nclass, "base", type.BaseType.FullName);
271 AddAttribute (nclass, "sealed", "true");
274 AddAttribute (nclass, "abstract", "true");
276 if (type.IsSerializable)
277 AddAttribute (nclass, "serializable", "true");
279 string charSet = GetCharSet (type);
280 AddAttribute (nclass, "charset", charSet);
282 string layout = GetLayout (type);
284 AddAttribute (nclass, "layout", layout);
286 parent.AppendChild (nclass);
288 AttributeData.OutputAttributes (document, nclass, type.GetCustomAttributes (false));
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) {
296 // we're only interested in public interfaces
299 XmlNode iface = document.CreateElement ("interface", null);
300 AddAttribute (iface, "name", t.FullName);
301 ifaces.AppendChild (iface);
305 ArrayList members = new ArrayList ();
307 FieldInfo[] fields = GetFields (type);
308 if (fields.Length > 0) {
309 Array.Sort (fields, MemberInfoComparer.Default);
310 FieldData fd = new FieldData (document, nclass, fields);
311 // Special case for enum fields
312 if (classType == "enum") {
313 string etype = fields [0].GetType ().FullName;
314 AddAttribute (nclass, "enumtype", etype);
319 ConstructorInfo [] ctors = GetConstructors (type);
320 if (ctors.Length > 0) {
321 Array.Sort (ctors, MemberInfoComparer.Default);
322 members.Add (new ConstructorData (document, nclass, ctors));
325 PropertyInfo[] properties = GetProperties (type);
326 if (properties.Length > 0) {
327 Array.Sort (properties, MemberInfoComparer.Default);
328 members.Add (new PropertyData (document, nclass, properties));
331 EventInfo [] events = GetEvents (type);
332 if (events.Length > 0) {
333 Array.Sort (events, MemberInfoComparer.Default);
334 members.Add (new EventData (document, nclass, events));
337 MethodInfo [] methods = GetMethods (type);
338 if (methods.Length > 0) {
339 Array.Sort (methods, MemberInfoComparer.Default);
340 members.Add (new MethodData (document, nclass, methods));
343 foreach (MemberData md in members)
346 Type [] nested = type.GetNestedTypes ();
347 if (nested != null && nested.Length > 0) {
348 XmlNode classes = document.CreateElement ("classes", null);
349 nclass.AppendChild (classes);
350 foreach (Type t in nested) {
351 TypeData td = new TypeData (document, classes, t);
357 protected override string GetMemberAttributes (MemberInfo member)
360 throw new InvalidOperationException ("odd");
362 return ((int) type.Attributes).ToString (CultureInfo.InvariantCulture);
365 public static bool MustDocumentMethod(MethodBase method)
368 return (method.IsPublic || method.IsFamily || method.IsFamilyOrAssembly);
371 static string GetClassType (Type t)
382 if (typeof (Delegate).IsAssignableFrom (t))
388 private static string GetCharSet (Type type)
390 if (type.IsAnsiClass)
391 return CharSet.Ansi.ToString (CultureInfo.InvariantCulture);
393 if (type.IsAutoClass)
394 return CharSet.Auto.ToString (CultureInfo.InvariantCulture);
396 if (type.IsUnicodeClass)
397 return CharSet.Unicode.ToString (CultureInfo.InvariantCulture);
399 return CharSet.None.ToString (CultureInfo.InvariantCulture);
402 private static string GetLayout (Type type)
404 if (type.IsAutoLayout)
405 return LayoutKind.Auto.ToString (CultureInfo.InvariantCulture);
407 if (type.IsExplicitLayout)
408 return LayoutKind.Explicit.ToString (CultureInfo.InvariantCulture);
410 if (type.IsLayoutSequential)
411 return LayoutKind.Sequential.ToString (CultureInfo.InvariantCulture);
416 private FieldInfo[] GetFields (Type type)
418 ArrayList list = new ArrayList ();
420 FieldInfo[] fields = type.GetFields (flags);
421 foreach (FieldInfo field in fields) {
422 if (field.IsSpecialName)
425 // we're only interested in public or protected members
426 if (!field.IsPublic && !field.IsFamily && !field.IsFamilyOrAssembly)
432 return (FieldInfo[]) list.ToArray (typeof (FieldInfo));
435 internal static PropertyInfo[] GetProperties (Type type)
437 ArrayList list = new ArrayList ();
439 PropertyInfo[] properties = type.GetProperties (flags);
440 foreach (PropertyInfo property in properties) {
441 MethodInfo getMethod = null;
442 MethodInfo setMethod = null;
444 if (property.CanRead) {
445 try { getMethod = property.GetGetMethod (true); }
446 catch (System.Security.SecurityException) { }
448 if (property.CanWrite) {
449 try { setMethod = property.GetSetMethod (true); }
450 catch (System.Security.SecurityException) { }
453 bool hasGetter = (getMethod != null) && MustDocumentMethod (getMethod);
454 bool hasSetter = (setMethod != null) && MustDocumentMethod (setMethod);
456 // if neither the getter or setter should be documented, then
458 if (!hasGetter && !hasSetter) {
465 return (PropertyInfo[]) list.ToArray (typeof (PropertyInfo));
468 private MethodInfo[] GetMethods (Type type)
470 ArrayList list = new ArrayList ();
472 MethodInfo[] methods = type.GetMethods (flags);
473 foreach (MethodInfo method in methods) {
474 if (method.IsSpecialName)
477 // we're only interested in public or protected members
478 if (!MustDocumentMethod(method))
484 return (MethodInfo[]) list.ToArray (typeof (MethodInfo));
487 private ConstructorInfo[] GetConstructors (Type type)
489 ArrayList list = new ArrayList ();
491 ConstructorInfo[] ctors = type.GetConstructors (flags);
492 foreach (ConstructorInfo constructor in ctors) {
493 // we're only interested in public or protected members
494 if (!constructor.IsPublic && !constructor.IsFamily && !constructor.IsFamilyOrAssembly)
497 list.Add (constructor);
500 return (ConstructorInfo[]) list.ToArray (typeof (ConstructorInfo));
503 private EventInfo[] GetEvents (Type type)
505 ArrayList list = new ArrayList ();
507 EventInfo[] events = type.GetEvents (flags);
508 foreach (EventInfo eventInfo in events) {
509 MethodInfo addMethod = eventInfo.GetAddMethod (true);
511 if (addMethod == null || !MustDocumentMethod (addMethod))
514 list.Add (eventInfo);
517 return (EventInfo[]) list.ToArray (typeof (EventInfo));
521 class FieldData : MemberData
523 public FieldData (XmlDocument document, XmlNode parent, FieldInfo [] members)
524 : base (document, parent, members)
528 protected override string GetName (MemberInfo member)
530 FieldInfo field = (FieldInfo) member;
534 protected override string GetMemberAttributes (MemberInfo member)
536 FieldInfo field = (FieldInfo) member;
537 return ((int) field.Attributes).ToString (CultureInfo.InvariantCulture);
540 protected override void AddExtraData (XmlNode p, MemberInfo member)
542 base.AddExtraData (p, member);
543 FieldInfo field = (FieldInfo) member;
544 AddAttribute (p, "fieldtype", field.FieldType.FullName);
546 if (field.IsLiteral) {
547 object value = field.GetValue (null);
548 string stringValue = null;
550 // FIXME: when Mono bug #60090 has been
551 // fixed, we should just be able to use
553 stringValue = ((Enum) value).ToString ("D", CultureInfo.InvariantCulture);
555 stringValue = Convert.ToString (value, CultureInfo.InvariantCulture);
558 if (stringValue != null)
559 AddAttribute (p, "value", stringValue);
563 public override string ParentTag {
564 get { return "fields"; }
567 public override string Tag {
568 get { return "field"; }
572 class PropertyData : MemberData
574 public PropertyData (XmlDocument document, XmlNode parent, PropertyInfo [] members)
575 : base (document, parent, members)
579 protected override string GetName (MemberInfo member)
581 PropertyInfo prop = (PropertyInfo) member;
585 protected override void AddExtraData (XmlNode p, MemberInfo member)
587 base.AddExtraData (p, member);
588 PropertyInfo prop = (PropertyInfo) member;
589 AddAttribute (p, "ptype", prop.PropertyType.FullName);
590 MethodInfo _get = prop.GetGetMethod (true);
591 MethodInfo _set = prop.GetSetMethod (true);
592 bool haveGet = (_get != null && TypeData.MustDocumentMethod(_get));
593 bool haveSet = (_set != null && TypeData.MustDocumentMethod(_set));
594 MethodInfo [] methods;
596 if (haveGet && haveSet) {
597 methods = new MethodInfo [] {_get, _set};
598 } else if (haveGet) {
599 methods = new MethodInfo [] {_get};
600 } else if (haveSet) {
601 methods = new MethodInfo [] {_set};
607 string parms = Parameters.GetSignature (methods [0].GetParameters ());
608 AddAttribute (p, "params", parms);
610 MethodData data = new MethodData (document, p, methods);
611 data.NoMemberAttributes = true;
615 protected override string GetMemberAttributes (MemberInfo member)
617 PropertyInfo prop = (PropertyInfo) member;
618 return ((int) prop.Attributes).ToString (CultureInfo.InvariantCulture);
621 public override string ParentTag {
622 get { return "properties"; }
625 public override string Tag {
626 get { return "property"; }
630 class EventData : MemberData
632 public EventData (XmlDocument document, XmlNode parent, EventInfo [] members)
633 : base (document, parent, members)
637 protected override string GetName (MemberInfo member)
639 EventInfo evt = (EventInfo) member;
643 protected override string GetMemberAttributes (MemberInfo member)
645 EventInfo evt = (EventInfo) member;
646 return ((int) evt.Attributes).ToString (CultureInfo.InvariantCulture);
649 protected override void AddExtraData (XmlNode p, MemberInfo member)
651 base.AddExtraData (p, member);
652 EventInfo evt = (EventInfo) member;
653 AddAttribute (p, "eventtype", evt.EventHandlerType.FullName);
656 public override string ParentTag {
657 get { return "events"; }
660 public override string Tag {
661 get { return "event"; }
665 class MethodData : MemberData
669 public MethodData (XmlDocument document, XmlNode parent, MethodBase [] members)
670 : base (document, parent, members)
674 protected override string GetName (MemberInfo member)
676 MethodBase method = (MethodBase) member;
677 string name = method.Name;
678 string parms = Parameters.GetSignature (method.GetParameters ());
679 return String.Format ("{0}({1})", name, parms);
682 protected override string GetMemberAttributes (MemberInfo member)
684 MethodBase method = (MethodBase) member;
685 return ((int) method.Attributes).ToString (CultureInfo.InvariantCulture);
688 protected override void AddExtraData (XmlNode p, MemberInfo member)
690 base.AddExtraData (p, member);
692 ParameterData parms = new ParameterData (document, p,
693 ((MethodBase) member).GetParameters ());
696 if (!(member is MethodInfo))
699 MethodInfo method = (MethodInfo) member;
700 AddAttribute (p, "returntype", method.ReturnType.FullName);
702 AttributeData.OutputAttributes (document, p,
703 method.ReturnTypeCustomAttributes.GetCustomAttributes (false));
706 public override bool NoMemberAttributes {
707 get { return noAtts; }
708 set { noAtts = value; }
711 public override string ParentTag {
712 get { return "methods"; }
715 public override string Tag {
716 get { return "method"; }
720 class ConstructorData : MethodData
722 public ConstructorData (XmlDocument document, XmlNode parent, ConstructorInfo [] members)
723 : base (document, parent, members)
727 public override string ParentTag {
728 get { return "constructors"; }
731 public override string Tag {
732 get { return "constructor"; }
736 class ParameterData : BaseData
738 private ParameterInfo[] parameters;
740 public ParameterData (XmlDocument document, XmlNode parent, ParameterInfo[] parameters)
741 : base (document, parent)
743 this.parameters = parameters;
746 public override void DoOutput ()
748 XmlNode parametersNode = document.CreateElement ("parameters", null);
749 parent.AppendChild (parametersNode);
751 foreach (ParameterInfo parameter in parameters) {
752 XmlNode paramNode = document.CreateElement ("parameter", null);
753 parametersNode.AppendChild (paramNode);
754 AddAttribute (paramNode, "name", parameter.Name);
755 AddAttribute (paramNode, "position", parameter.Position.ToString(CultureInfo.InvariantCulture));
756 AddAttribute (paramNode, "attrib", ((int) parameter.Attributes).ToString());
758 string direction = "in";
760 if (parameter.ParameterType.IsByRef) {
761 direction = parameter.IsOut ? "out" : "ref";
764 Type t = parameter.ParameterType;
765 AddAttribute (paramNode, "type", t.FullName);
767 if (parameter.IsOptional) {
768 AddAttribute (paramNode, "optional", "true");
769 if (parameter.DefaultValue != null)
770 AddAttribute (paramNode, "defaultValue", parameter.DefaultValue.ToString ());
773 if (direction != "in")
774 AddAttribute (paramNode, "direction", direction);
776 AttributeData.OutputAttributes (document, paramNode, parameter.GetCustomAttributes (false));
781 class AttributeData : BaseData
786 AttributeData (XmlDocument doc, XmlNode parent, object[] attributes, string target)
790 this.target = target;
793 AttributeData (XmlDocument doc, XmlNode parent, object [] attributes)
794 : this (doc, parent, attributes, null)
798 public override void DoOutput ()
800 if (document == null)
801 throw new InvalidOperationException ("Document not set");
803 if (atts == null || atts.Length == 0)
806 XmlNode natts = parent.SelectSingleNode("attributes");
808 natts = document.CreateElement ("attributes", null);
809 parent.AppendChild (natts);
812 for (int i = 0; i < atts.Length; ++i) {
813 Type t = atts [i].GetType ();
814 if (!t.IsPublic && !t.Name.EndsWith ("TODOAttribute"))
817 // we ignore attributes that inherit from SecurityAttribute on purpose as they:
818 // * aren't part of GetCustomAttributes in Fx 1.0/1.1;
819 // * are encoded differently and in a different metadata table; and
820 // * won't ever exactly match MS implementation (from a syntax pov)
821 if (t.IsSubclassOf (typeof (SecurityAttribute)))
824 XmlNode node = document.CreateElement ("attribute");
825 AddAttribute (node, "name", t.FullName);
827 XmlNode properties = null;
828 foreach (PropertyInfo pi in TypeData.GetProperties (t)) {
829 if (pi.Name == "TypeId")
832 if (properties == null) {
833 properties = node.AppendChild (document.CreateElement ("properties"));
837 object o = pi.GetValue (atts [i], null);
839 XmlNode n = properties.AppendChild (document.CreateElement ("property"));
840 AddAttribute (n, "name", pi.Name);
843 AddAttribute (n, "null", "true");
847 string value = o.ToString ();
848 if (t == typeof (GuidAttribute))
849 value = value.ToUpper ();
851 AddAttribute (n, "value", value);
853 catch (TargetInvocationException) {
858 if (target != null) {
859 AddAttribute (node, "target", target);
862 natts.AppendChild (node);
866 public static void OutputAttributes (XmlDocument doc, XmlNode parent, object[] attributes)
868 AttributeData ad = new AttributeData (doc, parent, attributes, null);
872 public static void OutputAttributes (XmlDocument doc, XmlNode parent, object [] attributes, string target)
874 AttributeData ad = new AttributeData (doc, parent, attributes, target);
878 private static bool MustDocumentAttribute (Type attributeType)
880 // only document MonoTODOAttribute and public attributes
881 return attributeType.Name.EndsWith ("TODOAttribute") || attributeType.IsPublic;
887 private Parameters () {}
889 public static string GetSignature (ParameterInfo [] infos)
891 if (infos == null || infos.Length == 0)
894 StringBuilder sb = new StringBuilder ();
895 foreach (ParameterInfo info in infos) {
899 else if (info.IsRetval)
906 string type_name = info.ParameterType.ToString ();
907 sb.AppendFormat ("{0}{1}, ", modifier, type_name);
910 sb.Length -= 2; // remove ", "
911 return sb.ToString ();
916 class TypeComparer : IComparer
918 public static TypeComparer Default = new TypeComparer ();
920 public int Compare (object a, object b)
924 int result = String.Compare (ta.Namespace, tb.Namespace);
928 return String.Compare (ta.Name, tb.Name);
932 class MemberInfoComparer : IComparer
934 public static MemberInfoComparer Default = new MemberInfoComparer ();
936 public int Compare (object a, object b)
938 MemberInfo ma = (MemberInfo) a;
939 MemberInfo mb = (MemberInfo) b;
940 return String.Compare (ma.Name, mb.Name);
944 class MethodBaseComparer : IComparer
946 public static MethodBaseComparer Default = new MethodBaseComparer ();
948 public int Compare (object a, object b)
950 MethodBase ma = (MethodBase) a;
951 MethodBase mb = (MethodBase) b;
952 int res = String.Compare (ma.Name, mb.Name);
956 ParameterInfo [] pia = ma.GetParameters ();
957 ParameterInfo [] pib = mb.GetParameters ();
958 if (pia.Length != pib.Length)
959 return pia.Length - pib.Length;
961 string siga = Parameters.GetSignature (pia);
962 string sigb = Parameters.GetSignature (pib);
963 return String.Compare (siga, sigb);