[Mono-docs-list] Updating class library docs

Joshua Tauberer tauberer@for.net
Thu, 04 Mar 2004 21:50:59 -0500


This is a multi-part message in MIME format.
--------------030608070604090500000708
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit

Miguel de Icaza wrote:
> Thanks a lot Joshua!

You're welcome.

Hector E. Gomez Morales wrote:
 > One question is that style of indentation in the docs intentional?

Forgot about that.  It's corrected in my stub.cs.

There's a weird bug in stub that I noticed today.  op_Explicit methods 
are getting added even if they're already in the docs.  There are a 
handful of those duplicated now in CVS. (oops)  Easily fixed when the 
bug is found, as I have stub now checking for duplicate entries.

But, I can't spend more time on this for now.  Attached is my stub.cs. 
There's a new command line option for updating an entire directory of 
XML files, which should make it easy for anyone to start updating other 
namespaces.

-- 
- Joshua Tauberer

http://taubz.for.net

** Nothing Unreal Exists **



--------------030608070604090500000708
Content-Type: text/c-sharp;
 name="stub.cs"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="stub.cs"

// Updater program for syncing Mono's ECMA-style documentation files
// with an assembly.
// By Joshua Tauberer <tauberer@for.net>

using System;
using System.Collections;
using System.Text;
using System.Reflection;
using System.Xml;

public class Stub {
	
	static XmlDocument doc = new XmlDocument();
	
	static int files = 0, additions = 0, deletions = 0;
	
	public static void Main(string[] args) {
		if (args.Length == 0) {
			Console.WriteLine("Usage: mono stub.exe command");
			Console.WriteLine("commands:");
			Console.WriteLine("\t--stub type\tCreates a type stub file on stdout.");
			Console.WriteLine("\t--update basedir type\tUpdates the file for type to stdout, with the file located in basedir/type.Namespace/type.Name.xml, by adding and removing new members as necessary.");
			Console.WriteLine("\t--regen basedir type\tCreates a type stub file on stdout, importing the Docs nodes from the file located in basedir/type.Namespace/type.Name.xml as appropriate.");
			Console.WriteLine("\t--updatens nsdir outdir\tUpdates the files in nsdir to outdir.");

			return;
		}
		
		switch (args[0]) {
			case "--stub":
				if (args.Length != 2) throw new InvalidOperationException();
				DoStubType(args[1]);
				break;
				
			case "--update":
				if (args.Length != 3) throw new InvalidOperationException();
				DoUpdateType(args[1], args[2]);
				break;

			case "--regen":
				if (args.Length != 3) throw new InvalidOperationException();
				DoRegenType(args[1], args[2]);
				break;

			case "--updatens":
				if (args.Length != 3) throw new InvalidOperationException();
				DoUpdateNS(args[1], args[2]);
				break;

			default:
				Console.WriteLine("Invalid command.  Run with no arguments for help.");
				break;
		}
		
		Console.Error.WriteLine("Files: {0}, Members Added: {1}, Members Deleted: {2}", files, additions, deletions);
	}
	
	private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
		XmlTextWriter writer = new XmlTextWriter(output);
		writer.Formatting = Formatting.Indented;
		writer.Indentation = 2;
		writer.IndentChar = ' ';
		element.WriteTo(writer);
		output.WriteLine();	
	}
	
	public static void DoStubType(string type) {
		WriteXml(StubType(type), Console.Out);
	}
	
	public static void DoUpdateType(string basepath, string typename) {
		Type type = Type.GetType(typename, true);
		
		XmlDocument basefile = new XmlDocument();
		basefile.PreserveWhitespace = true;
		basefile.Load(basepath + "/" + type.Namespace + "/" + type.Name + ".xml");
		
		DoUpdateType(basefile, type, Console.Out);
	}
	
	public static void DoUpdateNS(string nspath, string outpath) {
		foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
			XmlDocument basefile = new XmlDocument();
			basefile.PreserveWhitespace = true;
			basefile.Load(nspath + "/" + file.Name);

			string typename = basefile.SelectSingleNode("Type/@FullName").InnerText;
			Type type = Type.GetType(typename, false);
			if (type == null) {
				Console.Error.WriteLine(typename + " not found.");
				continue;
			}				

			using (System.IO.TextWriter output = new System.IO.StreamWriter(outpath + "/" + file.Name))
				DoUpdateType(basefile, type, output);
		}
	}

	public static void DoUpdateType(XmlDocument basefile, Type type, System.IO.TextWriter output) {
		bool mod = false;
		
		Console.Error.WriteLine("Updating: " + type.FullName);
		
		XmlElement newfile = StubType(type);
		
		Hashtable seenmembers = new Hashtable();
		
		// Look for deleted members, remember what members are in the file
		foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
			MemberInfo oldmember2 = GetMember(type, oldmember);
			
			if (oldmember2 == null) {
				// Deleted (or signature changed)
				Console.Error.WriteLine("Member Deleted: " + oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
				oldmember.ParentNode.RemoveChild(oldmember);
				deletions++; mod = true;
				continue;
			}
			
			if (seenmembers.ContainsKey(oldmember2)) {
				Console.Error.WriteLine("Duplicate member: " + oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
				oldmember.ParentNode.RemoveChild(oldmember);
				deletions++; mod = true;
				continue;
			}
			
			seenmembers[oldmember2] = 1;
		}

		if (!IsDelegate(type)) {
			Hashtable overrides = new Hashtable();
			foreach (Type i in type.GetInterfaces())
				foreach (MethodInfo m in type.GetInterfaceMap(i).TargetMethods)
					overrides[m] = 1;
			
			XmlNode members = basefile.SelectSingleNode("Type/Members");
			foreach (MemberInfo m in type.GetMembers(BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static|BindingFlags.Instance|BindingFlags.DeclaredOnly)) {
				if (m is Type) continue;
				if (seenmembers.ContainsKey(m)) continue;
				
				// To be nice on diffs, members/properties/events that are overrides or are interface implementations
				// are not added in.
				if (m is MethodInfo && !IsNew(overrides, (MethodInfo)m)) continue;
				if (m is PropertyInfo && !IsNew(overrides, ((PropertyInfo)m).GetGetMethod())) continue;
				if (m is PropertyInfo && !IsNew(overrides, ((PropertyInfo)m).GetSetMethod())) continue;
				if (m is EventInfo && !IsNew(overrides, ((EventInfo)m).GetAddMethod())) continue;
				if (m is EventInfo && !IsNew(overrides, ((EventInfo)m).GetRaiseMethod())) continue;
				if (m is EventInfo && !IsNew(overrides, ((EventInfo)m).GetRemoveMethod())) continue;
				
				XmlElement mm = MakeMember(m);
				if (mm == null) continue;
				members.AppendChild( basefile.ImportNode(mm, true) );
				members.AppendChild( members.OwnerDocument.CreateWhitespace("\n  ") );
	
				Console.Error.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
				additions++; mod = true;
			}
		}
		
		if (mod) files++;
		
		WriteXml(basefile.DocumentElement, output);
	}
	
	private static bool IsNew(Hashtable overrides, MethodInfo m) {
		if (m == null) return true;
		if (overrides.ContainsKey(m)) return false;
		MethodInfo b = m.GetBaseDefinition();
		if (b == null || b == m) return true;
		return false;
	}
	
	public static void DoRegenType(string basepath, string typename) {
		Type type = Type.GetType(typename, true);
		
		XmlDocument basefile = new XmlDocument();
		basefile.PreserveWhitespace = true;
		basefile.Load(basepath + "/" + type.Namespace + "/" + type.Name + ".xml");
		
		XmlElement newfile = StubType(type);
		
		// Copy old Type/Docs into new doc
		newfile.ReplaceChild(newfile.OwnerDocument.ImportNode(basefile.SelectSingleNode("Type/Docs"), true), newfile.SelectSingleNode("Docs"));
		
		// Copy old Type/Members/Member/Docs into new doc
		foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
			XmlElement newmember = FindMatchingMember(type, newfile, oldmember);
			if (newmember == null) {
				Console.Error.WriteLine("Member Deleted: " + oldmember.SelectSingleNode("MemberSignature/@Value").InnerText);
				continue;
			}
			newmember.ReplaceChild(newmember.OwnerDocument.ImportNode(oldmember.SelectSingleNode("Docs"), true), newmember.SelectSingleNode("Docs"));
		}

		WriteXml(newfile, Console.Out);
	}
	
	// UPDATE HELPER FUNCTIONS
	
	private static XmlElement FindMatchingMember(Type type, XmlElement newfile, XmlElement oldmember) {
		MemberInfo oldmember2 = GetMember(type, oldmember);
		if (oldmember2 == null) return null;
		
		string membername = oldmember.GetAttribute("MemberName");
		foreach (XmlElement newmember in newfile.SelectNodes("Members/Member[@MemberName='" + membername + "']")) {
			if (GetMember(type, newmember) == oldmember2) return newmember;
		}
		
		return null;
	}
	
	private static MemberInfo GetMember(Type type, XmlElement member) {
		string membertype = member.SelectSingleNode("MemberType").InnerText;
		
		// Get list of parameter types for member
		ArrayList memberparams = new ArrayList();
		foreach (XmlElement param in member.SelectNodes("Parameters/Parameter"))
			memberparams.Add(param.GetAttribute("Type"));
		
		// Loop through all members in this type with the same name
		MemberInfo[] mis = type.GetMember(member.GetAttribute("MemberName"), BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
		foreach (MemberInfo mi in mis) {
			if (mi is Type) continue;
			if (GetMemberType(mi) != membertype) continue;
			
			ParameterInfo[] pis = null;
			if (mi is MethodInfo || mi is ConstructorInfo)
				pis = ((MethodBase)mi).GetParameters();
			else if (mi is PropertyInfo)
				pis = ((PropertyInfo)mi).GetIndexParameters();
			
			if (pis == null)
				pis = new ParameterInfo[0];
				
			if (pis.Length != memberparams.Count) continue;			
			
			bool good = true;
			for (int i = 0; i < pis.Length; i++)
				if (pis[i].ParameterType.FullName != (string)memberparams[i]) { good = false; break; }
			if (!good) continue;

			return mi;
		}
		
		return null;
	}
	
	// CREATE A STUB OF A DOCUMENTATION FILE	

	public static XmlElement StubType(string type) {
		return StubType(Type.GetType(type, true));
	}
	
	public static XmlElement StubType(Type type) {
		string typesig = MakeTypeSignature(type);
		if (typesig == null) return null; // not publicly visible
		
		XmlElement root = doc.CreateElement("Type");
		root.SetAttribute("Name", type.Name);
		root.SetAttribute("FullName", type.FullName);
		
		XmlElement sig = doc.CreateElement("TypeSignature");
		root.AppendChild(sig);
		sig.SetAttribute("Language", "C#");
		sig.SetAttribute("Value", typesig);
		
		XmlElement ass = doc.CreateElement("AssemblyInfo");
		root.AppendChild(ass);
		ass.AppendChild(SimpleElement("AssemblyName", type.Assembly.GetName().Name));
		ass.AppendChild(SimpleElement("AssemblyVersion", type.Assembly.GetName().Version.ToString()));
		ass.AppendChild(SimpleElement("AssemblyCulture", type.Assembly.GetName().CultureInfo.Name));
		XmlElement assattributes = MakeAttributes(type.Assembly);
		if (assattributes != null) ass.AppendChild(assattributes);
		
		if (type.BaseType != null) {
			XmlElement basetype = doc.CreateElement("Base");
			root.AppendChild(basetype);
			basetype.AppendChild(SimpleElement("BaseTypeName", type.BaseType.FullName));
		}

		if (!IsDelegate(type) && !type.IsInterface) {
			XmlElement interfaces = doc.CreateElement("Interfaces");
			root.AppendChild(interfaces);
			foreach (Type i in type.GetInterfaces()) {
				XmlElement iface = doc.CreateElement("Interface");
				interfaces.AppendChild(iface);
				iface.AppendChild(SimpleElement("InterfaceName", i.FullName));
			}
		}

		XmlElement attributes = MakeAttributes(type);
		if (attributes != null) root.AppendChild(attributes);
		
		if (IsDelegate(type)) {
			root.AppendChild(MakeParameters(type.GetMethod("Invoke").GetParameters()));
			root.AppendChild(MakeReturnValue(type.GetMethod("Invoke")));
		}
		
		if (!IsDelegate(type)) {
			XmlElement members = doc.CreateElement("Members");
			root.AppendChild(members);
			foreach (MemberInfo m in type.GetMembers(BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static|BindingFlags.Instance|BindingFlags.DeclaredOnly)) {
				if (m is Type) continue;
				XmlElement mm = MakeMember(m);
				if (mm == null) continue;
				members.AppendChild( mm );
			}
		}
		
		root.AppendChild(MakeDocNode(null, null, true));
		
		return root;
	}
	
	// STUB HELPER FUNCTIONS
	
	private static XmlElement SimpleElement(string name, string val) {
		XmlElement e = doc.CreateElement(name);
		e.InnerText = val;
		return e;
	}
	
	private static XmlElement MakeDocNode(ParameterInfo[] parameters, Type returntype, bool returnisreturn) {
		XmlElement e = doc.CreateElement("Docs");
		
		e.AppendChild(SimpleElement("summary", "To be added."));
		
		if (parameters != null) {
			foreach (ParameterInfo p in parameters) {
				XmlElement pe = SimpleElement("param", "To be added.");
				pe.SetAttribute("name", p.Name);
				e.AppendChild(pe);
			}
		}
		
		if (returntype != null && returntype != typeof(void)) {
			e.AppendChild(SimpleElement(returnisreturn ? "returns" : "value", "To be added."));
		}

		e.AppendChild(SimpleElement("remarks", "To be added."));
		
		return e;
	}
	
	private static XmlElement MakeAttributes(ICustomAttributeProvider attributes) {
		object[] at = attributes.GetCustomAttributes(false);
		if (at.Length == 0) return null;

		bool b = false;
		XmlElement e = doc.CreateElement("Attributes");
		foreach (Attribute a in at) {
			if (GetTypeVisibility(a.GetType().Attributes) == null) continue; // hide non-visible attributes
			b = true;
			
			// There's no way to reconstruct how the attribute's constructor was called,
			// so as a substitute, just list the value of all of the attribute's public fields.
			
			ArrayList fields = new ArrayList();
			foreach (PropertyInfo f in a.GetType().GetProperties(BindingFlags.Public|BindingFlags.Instance)) {
				if (f.Name == "TypeId") continue;
				
				object v = f.GetValue(a, null);
				if (v == null) v = "null";
				else if (v is string) v = "\"" + v + "\"";
				else if (v is Type) v = "typeof(" + ((Type)v).FullName + ")";
				else if (v is Enum) v = v.GetType().FullName + "." + v;
					
				fields.Add(f.Name + "=" + v);
			}
			string a2 = String.Join(", ", (string[])fields.ToArray(typeof(string)));
			if (a2 != "") a2 = "(" + a2 + ")";
			
			XmlElement ae = doc.CreateElement("Attribute");
			e.AppendChild(ae);
			
			string name = a.GetType().FullName;
			if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
			ae.AppendChild( SimpleElement("AttributeName", name + a2) );
		}
		
		if (!b) return null;
		
		return e;
	}
	
	private static XmlElement MakeParameters(ParameterInfo[] parameters) {
		XmlElement e = doc.CreateElement("Parameters");
		foreach (ParameterInfo p in parameters) {
			XmlElement pe = doc.CreateElement("Parameter");
			e.AppendChild(pe);
			pe.SetAttribute("Name", p.Name);
			pe.SetAttribute("Type", p.ParameterType.FullName);
			if (p.ParameterType.IsByRef) {
				if (p.IsOut) pe.SetAttribute("RefType", "out");
				else pe.SetAttribute("RefType", "ref");
			}
			XmlElement attributes = MakeAttributes(p);
			if (attributes != null) pe.AppendChild(attributes);
		}
		return e;
	}

	private static XmlElement MakeParameters(MemberInfo mi) {
		if (mi is ConstructorInfo) return MakeParameters(((ConstructorInfo)mi).GetParameters());
		if (mi is MethodInfo) return MakeParameters(((MethodInfo)mi).GetParameters());
		if (mi is PropertyInfo) {
			ParameterInfo[] parameters = ((PropertyInfo)mi).GetIndexParameters();
			if (parameters.Length > 0)
				return MakeParameters(parameters);
			else
				return null;
		}
		if (mi is FieldInfo) return null;
		if (mi is EventInfo) return null;
		throw new ArgumentException();
	}

	private static XmlElement MakeReturnValue(Type type, ICustomAttributeProvider attributes) {
		XmlElement e = doc.CreateElement("ReturnValue");
		e.AppendChild( SimpleElement("ReturnType", type.FullName) );
		if (attributes != null) {
			XmlElement a = MakeAttributes(attributes);
			if (a != null) e.AppendChild(a);
		}
		return e;
	}
	
	private static XmlElement MakeReturnValue(MemberInfo mi) {
		if (mi is ConstructorInfo) return null;
		if (mi is MethodInfo) return MakeReturnValue(((MethodInfo)mi).ReturnType, ((MethodInfo)mi).ReturnTypeCustomAttributes);
		if (mi is PropertyInfo) return MakeReturnValue(((PropertyInfo)mi).PropertyType, null);
		if (mi is FieldInfo) return MakeReturnValue(((FieldInfo)mi).FieldType, null);
		if (mi is EventInfo) return MakeReturnValue(((EventInfo)mi).EventHandlerType, null);
		throw new ArgumentException(mi.GetType().FullName);
	}

	private static XmlElement MakeMember(MemberInfo mi) {
		if (mi is Type) return null;
		
		string sigs = MakeMemberSignature(mi);
		if (sigs == null) return null; // not publicly visible
		
		// no documentation for property/event accessors.  Is there a better way of doing this?
		if (mi.Name.StartsWith("get_")) return null;
		if (mi.Name.StartsWith("set_")) return null;
		if (mi.Name.StartsWith("add_")) return null;
		if (mi.Name.StartsWith("remove_")) return null;
		if (mi.Name.StartsWith("raise_")) return null;
		
		XmlElement me = doc.CreateElement("Member");
		me.SetAttribute("MemberName", mi.Name);
		
		XmlElement sig = doc.CreateElement("MemberSignature");
		me.AppendChild(sig);
		sig.SetAttribute("Language", "C#");
		sig.SetAttribute("Value", sigs);
		
		me.AppendChild( SimpleElement("MemberType", GetMemberType(mi)) );
		
		XmlElement a = MakeAttributes(mi);
		if (a != null) me.AppendChild(a);
		
		XmlElement retval = MakeReturnValue(mi);
		if (retval != null) me.AppendChild(retval);
		
		XmlElement parameters = MakeParameters(mi);
		if (parameters != null) me.AppendChild(parameters);
		
		if (mi is FieldInfo && (((FieldInfo)mi).IsLiteral || (((FieldInfo)mi).IsStatic && ((FieldInfo)mi).IsInitOnly))) {
			object val = ((FieldInfo)mi).GetValue(null);
			if (val == null) val = "null";
			else if (val is Enum) val = val.ToString();
			else if (val is IFormattable) val = ((IFormattable)val).ToString();
			if (val is string && (string)val != "")
				me.AppendChild( SimpleElement("MemberValue", (string)val) );
		}
		
		if (mi is MethodInfo)
			me.AppendChild(MakeDocNode(((MethodInfo)mi).GetParameters(), ((MethodInfo)mi).ReturnType, true));
		else if (mi is ConstructorInfo)
			me.AppendChild(MakeDocNode(((ConstructorInfo)mi).GetParameters(), null, false));
		else if (mi is PropertyInfo)
			me.AppendChild(MakeDocNode(((PropertyInfo)mi).GetIndexParameters(), ((PropertyInfo)mi).PropertyType, false));
		else
			me.AppendChild(MakeDocNode(null, null, false));

		return me;
	}
	
	static bool IsDelegate(Type type) {
		return typeof(System.Delegate).IsAssignableFrom (type) && !type.IsAbstract;
	}
	
	/// SIGNATURE GENERATION FUNCTIONS
	
	static string GetTypeKind (Type t) {
		if (t.IsEnum) return "enum";
		if (t.IsClass || t == typeof(System.Enum)) return "class";
		if (t.IsInterface) return "interface";
		if (t.IsValueType) return "struct";
		throw new ArgumentException(t.FullName);
	}

	static string GetTypeVisibility (TypeAttributes ta) {
		switch (ta & TypeAttributes.VisibilityMask){
		case TypeAttributes.Public:
		case TypeAttributes.NestedPublic:
				return "public";

		case TypeAttributes.NestedFamily:
		case TypeAttributes.NestedFamORAssem:
				return "protected";

		default:
				return null;
		}
	}

	static string MakeTypeSignature (Type type) {
		StringBuilder sig = new StringBuilder();
		
		string visibility = GetTypeVisibility(type.Attributes);
		if (visibility == null) return null;
		sig.Append(visibility);
		sig.Append(" ");

		if (type.IsAbstract) sig.Append("abstract ");
		if (type.IsSealed && !IsDelegate(type)) sig.Append("sealed ");

		if (IsDelegate(type)) {
			MethodInfo invoke = type.GetMethod ("Invoke");
			string arguments = GetMethodParameters(invoke.GetParameters());
			string return_value = ConvertCTSName(invoke.ReturnType.FullName);
			sig.Append(return_value);
			sig.Append(" ");
			sig.Append(type.Name);
			sig.Append("(");
			sig.Append(arguments);
			sig.Append(")");
			return sig.ToString();
		}
		
		sig.Append(GetTypeKind(type));
		sig.Append(" ");
		sig.Append(type.Name);

		if (!type.IsValueType && !type.IsEnum) {
			if ((type.BaseType != null && type.BaseType != typeof(object)) || type.GetInterfaces().Length > 0)
				sig.Append(" : ");
			
			if (type.BaseType != null && type.BaseType != typeof(object)) {
				sig.Append(type.BaseType.FullName);
				if (type.GetInterfaces().Length > 0)
					sig.Append(", ");
			}
			
			Type [] interfaces = type.GetInterfaces ();
			for (int i = 0; i < interfaces.Length; i ++){
					if (i != 0) sig.Append(", ");
					sig.Append(interfaces [i].FullName);
			}
		}

		return sig.ToString();
	}


	static string GetFieldVisibility (FieldInfo field) {
		if (field.IsPublic) return "public";
		if (field.IsFamily) return "protected";
		return null;
	}

	static string MakeFieldSignature (FieldInfo field) {
		string visibility = GetFieldVisibility (field);
		if (visibility == null) return null;
		
		
		string type = ConvertCTSName (field.FieldType.FullName);
		
		string modifiers = String.Empty;
		if (field.IsStatic) modifiers += " static";
		if (field.IsInitOnly) modifiers += " readonly";
		if (field.IsLiteral) modifiers += " const";

		return String.Format ("{0}{1} {2} {3}",
						visibility, modifiers, type, field.Name);
	}

	static string GetMethodVisibility (MethodBase method) {
		if (method.IsPublic) return "public";
		if (method.IsFamily) return "protected";
		return null;
	}

	static string GetMethodParameters (ParameterInfo[] pi) {
		if (pi.Length == 0) return "";
		
		StringBuilder sb = new StringBuilder ();

		int i = 0;
		string modifier;
		foreach (ParameterInfo parameter in pi) {
			if (i != 0) sb.Append (", ");
			if (parameter.ParameterType.IsByRef) {
				if (parameter.IsOut) sb.Append("out ");
				else sb.Append("ref ");
			}
			string param = ConvertCTSName (parameter.ParameterType.FullName, parameter.ParameterType.IsByRef);
			sb.Append (param);
			sb.Append (" ");
			sb.Append (parameter.Name);
			i++;
		}

		return sb.ToString();
	}

	static string MakeMethodSignature (MethodInfo method) {
		string visibility = GetMethodVisibility (method);
		if (visibility == null)
				return null;
		
		string modifiers = String.Empty;
		if (method.IsStatic) modifiers += " static";
		if (method.IsVirtual) {
				if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
				else modifiers += " override";
		}
		if (method.IsAbstract) modifiers += " abstract";
		if (method.IsFinal) modifiers += " sealed";
		if (modifiers == " virtual sealed") modifiers = "";

		string return_type = ConvertCTSName (method.ReturnType.FullName);
		string parameters = GetMethodParameters (method.GetParameters());

		string method_name = method.Name;
		
		// operators, default accessors need name rewriting

		return String.Format ("{0}{1} {2} {3}({4})",
						visibility, modifiers, return_type, method_name, parameters);
	}

	static string MakeConstructorSignature (ConstructorInfo constructor) {
		string visibility = GetMethodVisibility (constructor);
		if (visibility == null)
			return null;

		string name = constructor.DeclaringType.Name;
		string parameters = GetMethodParameters (constructor.GetParameters());

		return String.Format ("{0} {1}({2})",
						visibility, name, parameters);
	}


	static string MakePropertySignature (PropertyInfo property) {
		// pick an accessor
		MethodBase method = property.GetSetMethod (true);
		if (method == null)
			method = property.GetGetMethod (true);
	
		string visibility = GetMethodVisibility(method);
		if (visibility == null)
			return null;
	
		string modifiers = String.Empty;
		if (method.IsStatic) modifiers += " static";
		if (method.IsVirtual) {
				if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
				else modifiers += " override";
		}
		if (method.IsAbstract) modifiers += " abstract";
		if (method.IsFinal) modifiers += " sealed";
		if (modifiers == " virtual sealed") modifiers = "";
	
		string name = property.Name;
	
		string type_name = property.PropertyType.FullName;
		type_name = ConvertCTSName (type_name);
		
		string parameters = GetMethodParameters (property.GetIndexParameters());
		if (parameters != "") parameters = "[" + parameters + "]";		
		
		string accessors = null;
		if (property.CanRead && property.CanWrite)
			accessors = "{ set; get; }";
		else if (property.CanRead)
			accessors = "{ get; }";
		else if (property.CanWrite)
			accessors = "{ set; }";
	
		return String.Format ("{0}{1} {2} {3}{4} {5}",
						visibility, modifiers, type_name, name, parameters, accessors);
	}
		
	static string MakeEventSignature (EventInfo ev) {
		MethodInfo add = ev.GetAddMethod ();

		string visibility = GetMethodVisibility(add);
		if (visibility == null)
			return null;

		string modifiers = String.Empty;
		if (add.IsStatic) modifiers += " static";
		if (add.IsVirtual) {
				if ((add.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
				else modifiers += " override";
		}
		if (add.IsAbstract) modifiers += " abstract";
		if (add.IsFinal) modifiers += " sealed";
		if (modifiers == " virtual sealed") modifiers = "";
		
		string name = ev.Name;
		string type = ConvertCTSName(ev.EventHandlerType.FullName);

		return String.Format ("{0}{1} event {2} {3}",
						visibility, modifiers, type, name);
	}
	
	static string MakeMemberSignature(MemberInfo mi) {
		if (mi is ConstructorInfo) return MakeConstructorSignature((ConstructorInfo)mi);
		if (mi is MethodInfo) return MakeMethodSignature((MethodInfo)mi);
		if (mi is PropertyInfo) return MakePropertySignature((PropertyInfo)mi);
		if (mi is FieldInfo) return MakeFieldSignature((FieldInfo)mi);
		if (mi is EventInfo) return MakeEventSignature((EventInfo)mi);
		throw new ArgumentException(mi.ToString());
	}

	static string GetMemberType(MemberInfo mi) {
		if (mi is ConstructorInfo) return "Constructor";
		if (mi is MethodInfo) return "Method";
		if (mi is PropertyInfo) return "Property";
		if (mi is FieldInfo) return "Field";
		if (mi is EventInfo) return "Event";
		throw new ArgumentException();
	}

	static string ConvertCTSName (string type, bool shorten)
	{
			if (shorten)
					type =  type.Substring (0, type.Length - 1);

			string retval =  ConvertCTSName (type);

			return retval;
	}

	//
	// Utility function: converts a fully .NET qualified type name into a C#-looking one
	//
	static string ConvertCTSName (string type) {
		if (type.EndsWith ("[]"))
			return ConvertCTSName(type.Substring(0, type.Length - 2).TrimEnd()) + "[]";

		if (type.EndsWith ("&"))
			return ConvertCTSName(type.Substring(0, type.Length - 1).TrimEnd()) + "&";

		if (type.EndsWith ("*"))
			return ConvertCTSName(type.Substring(0, type.Length - 1).TrimEnd()) + "*";

		if (!type.StartsWith ("System."))
				return type;

		switch (type) {
		case "System.Byte": return "byte";
		case "System.SByte": return "sbyte";
		case "System.Int16": return "short";
		case "System.Int32": return "int";
		case "System.Int64": return "long";

		case "System.UInt16": return "ushort";
		case "System.UInt32": return "uint";
		case "System.UInt64": return "ulong";

		case "System.Single":  return "float";
		case "System.Double":  return "double";
		case "System.Decimal": return "decimal";
		case "System.Boolean": return "bool";
		case "System.Char":    return "char";
		case "System.Void":    return "void";
		case "System.String":  return "string";
		case "System.Object":  return "object";
		}

		return type;
	}
	

}

--------------030608070604090500000708--