GtkSharp/doc/introspect.cs

299 lines
8.5 KiB
C#
Raw Normal View History

// introspect.cs: Add introspectable information to a docs file.
//
// Author: Rachel Hestilow <hestilow@ximian.com>
//
// (c) 2002 Rachel Hestilow
namespace GtkSharp.DocGeneration {
using System;
using System.Collections;
using System.IO;
using System.Reflection;
using System.Xml;
public class Introspect {
Hashtable assemblies = new Hashtable ();
Hashtable primitives = new Hashtable ();
XmlDocument doc;
string current_ns;
public Introspect (XmlDocument doc) {
this.doc = doc;
primitives["System.Boolean"] = "bool";
primitives["System.Byte"] = "byte";
primitives["System.Char"] = "char";
primitives["System.Decimal"] = "decimal";
primitives["System.Double"] = "double";
primitives["System.Int16"] = "int16";
primitives["System.Int32"] = "int";
primitives["System.Int64"] = "int64";
primitives["System.Object"] = "object";
primitives["System.SByte"] = "sbyte";
primitives["System.Single"] = "single";
primitives["System.String"] = "string";
primitives["System.UInt16"] = "uint16";
primitives["System.UInt32"] = "uint";
primitives["System.UInt64"] = "uint64";
primitives["System.Void"] = "void";
/* FIXME: mcs does not support Assembly.GetReferencedAssemblies*/
foreach (string asm in new string[] {"glib", "atk", "pango", "gdk"}) {
string key = asm + "-sharp";
assemblies[key] = Assembly.Load (key);
}
}
public Type LookupType (string typename)
{
foreach (Assembly assembly in assemblies.Values) {
Type type = assembly.GetType (typename);
if (type != null)
return type;
}
return Type.GetType (typename);
}
public string StringifyType (Type type)
{
string full = type.ToString ();
bool isArray;
if (full.EndsWith ("[]")) {
full = full.Substring (0, full.Length - 2);
isArray = true;
} else
isArray = false;
if (primitives.Contains (full)) {
string ret = (string) primitives[full];
if (isArray) ret += "[]";
return ret;
} else {
if (String.Compare (full, 0, current_ns, 0, current_ns.Length) == 0) {
full = full.Substring (current_ns.Length + 1);
}
return full;
}
}
public void FixArgs (XmlElement method_node, Type type, Assembly asm, string method_name, string orig, bool isCtor)
{
XmlNode args_node = doc.CreateNode ("element", "arguments", "");
method_node.AppendChild (args_node);
if (orig == "") return;
string[] args = orig.Split (',');
Type[] signature = new Type[args.Length];
int i = 0;
foreach (string arg in args) {
string fix = arg.Trim ('@');
signature[i] = LookupType (fix);
i++;
}
MethodBase method = null;
MethodBase[] methods;
if (isCtor)
{
MemberInfo[] bases = type.FindMembers (MemberTypes.Constructor | MemberTypes.Method, BindingFlags.Public | BindingFlags.Instance, Type.FilterName, ".ctor");
ArrayList ctors = new ArrayList ();
foreach (MemberInfo info in bases) {
if (info.MemberType == MemberTypes.Constructor)
ctors.Add (info);
}
methods = new MethodBase[ctors.Count];
ctors.CopyTo (methods);
}
else
methods = type.GetMethods ();
foreach (MethodBase m in methods) {
if (m.GetParameters () == null)
continue;
if (m.Name != method_name)
continue;
if (m.GetParameters ().Length != signature.Length)
continue;
bool valid = true;
for (i = 0; i < signature.Length; i++) {
// FIXME: cludge
string t1 = m.GetParameters ()[i].ParameterType.FullName;
t1 = t1.Trim ('&');
if (t1 != signature[i].FullName) {
valid = false;
break;
}
}
if (!valid) continue;
method = m;
break;
}
i = 0;
foreach (ParameterInfo p in method.GetParameters ()) {
string modifiers = "";
// FIXME: another mono bug...this is always false
if (p.IsOut || p.IsRetval)
modifiers += "out ";
XmlElement arg_node = (XmlElement) doc.CreateNode ("element", "argument", "");
args_node.AppendChild (arg_node);
arg_node.SetAttribute ("modifiers", modifiers);
arg_node.SetAttribute ("type", StringifyType (signature[i]));
arg_node.SetAttribute ("name", p.Name);
i++;
}
}
public void FixProperty (XmlElement prop, Assembly asm, Type type)
{
PropertyInfo[] props = type.GetProperties (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
string prop_name = prop.GetAttribute ("name");
PropertyInfo pinfo = null;
foreach (PropertyInfo i in props) {
if (i.Name == prop_name) {
pinfo = i;
break;
}
}
// FIXME: mono bug: why is PropertyType sometimes null?
if (pinfo.PropertyType != null)
prop.SetAttribute ("type", StringifyType (pinfo.PropertyType));
else
prop.SetAttribute ("type", "[unknown]");
}
public void FixMethod (XmlElement method, Assembly asm, Type type)
{
MethodInfo[] methods = type.GetMethods (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
string method_name = method.GetAttribute ("name");
string op = "op_Explicit -> ";
if (String.Compare (method_name, 0, op, 0, op.Length) == 0) {
method.SetAttribute ("type", StringifyType (LookupType (method_name.Substring (op.Length))));
method.SetAttribute ("name", "op_Explicit");
return;
}
FixArgs (method, type, asm, method_name, method.GetAttribute ("args"), false);
MethodInfo minfo = null;
foreach (MethodInfo i in methods) {
if (i.Name == method_name) {
minfo = i;
break;
}
}
method.SetAttribute ("type", StringifyType (minfo.ReturnType));
}
public void FixConstructor (XmlElement method, Assembly asm, Type type)
{
string method_name = method.GetAttribute ("name");
FixArgs (method, type, asm, ".ctor", method.GetAttribute ("args"), true);
}
public bool BaseClassImplements (Type type, Type target)
{
if (type.BaseType == Type.GetType ("System.Object"))
return false;
foreach (Type iface in type.BaseType.GetInterfaces ()) {
if (iface == target)
return true;
}
return BaseClassImplements (type.BaseType, target);
}
public void FixClass (XmlElement klass, string ns)
{
current_ns = ns;
string asm = klass.GetAttribute ("assembly");
Assembly assembly;
if (assemblies.Contains (asm))
assembly = (Assembly) assemblies[asm];
else {
assembly = Assembly.Load (asm);
assemblies[asm] = assembly;
}
Type type = LookupType (ns + "." + klass.GetAttribute ("name"));
if (type.BaseType != null && type.BaseType != Type.GetType ("System.Object") && type.BaseType != Type.GetType ("System.Enum"))
klass.SetAttribute ("base", StringifyType (type.BaseType));
Type[] unfiltered = type.GetInterfaces ();
ArrayList ifaces = new ArrayList ();
if (unfiltered != null && unfiltered.Length > 0) {
foreach (Type iface in unfiltered) {
if (type.BaseType == null || !BaseClassImplements (type, iface))
ifaces.Add (iface);
}
}
if (ifaces.Count > 0) {
XmlNode implements = doc.CreateNode ("element", "implements", "");
klass.AppendChild (implements);
foreach (Type iface in ifaces) {
XmlNode iface_node = doc.CreateNode ("element", "interface", "");
implements.AppendChild (iface_node);
XmlText text = (XmlText) doc.CreateNode ("text", "", "");
text.Value = StringifyType (iface);
iface_node.AppendChild (text);
}
}
foreach (XmlNode member in klass.ChildNodes) {
switch (member.Name) {
case "property":
FixProperty ((XmlElement) member, assembly, type);
break;
case "method":
FixMethod ((XmlElement) member, assembly, type);
break;
case "constructor":
FixConstructor ((XmlElement) member, assembly, type);
break;
default:
break;
}
}
}
public static int Main (string[] args)
{
if (args.Length != 2) {
Console.WriteLine ("usage: introspect <infile> <outfile>");
return 0;
}
XmlDocument doc = new XmlDocument ();
try {
doc.Load (args[0]);
} catch (XmlException e) {
Console.WriteLine ("Failed to load {0}", args[0]);
return 1;
}
Introspect introspector = new Introspect (doc);
XmlElement root = doc.DocumentElement;
foreach (XmlNode ns in root.ChildNodes) {
if (ns.Name != "namespace") continue;
XmlElement ns_elem = (XmlElement) ns;
foreach (XmlNode klass in ns.ChildNodes) {
if (klass.Name != "class") continue;
introspector.FixClass ((XmlElement) klass, ns_elem.GetAttribute ("name"));
}
}
FileStream stream = new FileStream (args[1], FileMode.Create, FileAccess.Write);
doc.Save (stream);
return 0;
}
}
}