1 // Updater program for syncing Mono's ECMA-style documentation files
3 // By Joshua Tauberer <tauberer@for.net>
6 using System.Collections;
8 using System.Collections.Generic;
10 using System.Globalization;
13 using System.Reflection;
15 using System.Xml.XPath;
17 using Mono.GetOptions;
20 using MemberInfoEnumerable = System.Collections.IEnumerable;
21 using MyXmlNodeList = System.Collections.ArrayList;
22 using StringList = System.Collections.ArrayList;
23 using StringToStringMap = System.Collections.Hashtable;
24 using StringToXmlNodeMap = System.Collections.Hashtable;
26 using MemberInfoEnumerable = System.Collections.Generic.IEnumerable<System.Reflection.MemberInfo>;
27 using MyXmlNodeList = System.Collections.Generic.List<System.Xml.XmlNode>;
28 using StringList = System.Collections.Generic.List<string>;
29 using StringToStringMap = System.Collections.Generic.Dictionary<string, string>;
30 using StringToXmlNodeMap = System.Collections.Generic.Dictionary<string, System.Xml.XmlNode>;
33 [assembly: AssemblyTitle("Monodocer - The Mono Documentation Tool")]
34 [assembly: AssemblyCopyright("Copyright (c) 2004 Joshua Tauberer <tauberer@for.net>\nreleased under the GPL.")]
35 [assembly: AssemblyDescription("A tool for creating and updating Mono XML documentation files for assemblies.")]
37 [assembly: Mono.UsageComplement("")]
39 namespace Mono.Documentation {
40 public class Updater {
42 static string srcPath;
43 static Assembly[] assemblies;
45 static bool nooverrides = true, delete = false, ignoremembers = false;
46 static bool pretty = false;
47 static bool show_exceptions = false;
49 static int additions = 0, deletions = 0;
52 static XmlDocument slashdocs;
53 static XmlReader ecmadocs;
57 static MemberFormatter csharpFullFormatter = new CSharpFullMemberFormatter ();
58 static MemberFormatter csharpFormatter = new CSharpMemberFormatter ();
59 static MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
60 static MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
61 static MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
63 static MyXmlNodeList extensionMethods = new MyXmlNodeList ();
65 const BindingFlags DefaultBindingFlags =
66 BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly;
68 private class Opts : Options {
69 [Option("The root {directory} of an assembly's documentation files.")]
70 public string path = null;
72 [Option("When updating documentation, write the updated files to this {path}.")]
73 public string updateto = null;
75 [Option(-1, "The assembly to document. Specify a {file} path or the name of a GAC'd assembly.")]
76 public string[] assembly = null;
78 [Option(-1, "Document only the {type name}d by this argument.")]
79 public string[] type = null;
81 [Option("Update only the types in this {namespace}.")]
82 public string @namespace = null;
84 [Option("Allow monodocer to delete members from files.")]
85 public bool delete = false;
87 [Option("Include overridden methods in documentation.")]
88 public bool overrides = false;
90 [Option("Don't update members.")]
91 public bool ignoremembers = false;
93 [Option("Don't rename documentation XML files for missing types. IGNORED.")]
94 public bool ignore_extra_docs = false;
96 [Option("The {name} of the project this documentation is for.")]
99 [Option("An XML documentation {file} made by the /doc option of mcs/csc the contents of which will be imported.")]
100 public string importslashdoc;
102 [Option("An ECMA or monodoc-generated XML documemntation {file} to import.")]
103 public string importecmadoc;
105 [Option("Import either a /doc or ECMA documentation file.")]
106 public string import;
108 [Option("Indent the XML files nicely.")]
109 public bool pretty = false;
111 [Option("Create a <since/> element for added types/members with the value {since}.")]
114 [Option("Show full stack trace on error.")]
115 public bool show_exceptions;
118 public static void Main(string[] args) {
119 Opts opts = new Opts();
120 opts.ProcessArgs(args);
122 if (args.Length == 0) {
127 nooverrides = !opts.overrides;
128 delete = opts.delete;
129 ignoremembers = opts.ignoremembers;
131 pretty = opts.pretty;
133 show_exceptions = opts.show_exceptions;
136 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
138 if (opts.path == null)
139 throw new InvalidOperationException("The path option is required.");
143 if (opts.type != null && opts.type.Length > 0 && opts.@namespace != null)
144 throw new InvalidOperationException("You cannot specify both 'type' and 'namespace'.");
146 if (opts.assembly == null)
147 throw new InvalidOperationException("The assembly option is required.");
149 assemblies = new Assembly [opts.assembly.Length];
150 for (int i = 0; i < opts.assembly.Length; i++)
151 assemblies [i] = LoadAssembly (opts.assembly [i]);
155 if (opts.importslashdoc != null) {
157 slashdocs = new XmlDocument();
158 slashdocs.Load(opts.importslashdoc);
159 } catch (Exception e) {
160 Error ("Could not load /doc file: {0}", e.Message);
161 Environment.ExitCode = 1;
166 if (opts.importecmadoc != null) {
168 ecmadocs = new XmlTextReader (opts.importecmadoc);
169 } catch (Exception e) {
170 Error ("Could not load ECMA XML file: {0}", e.Message);
171 Environment.ExitCode = 1;
176 if (opts.import != null && ecmadocs == null && slashdocs == null) {
178 XmlReader r = new XmlTextReader (opts.import);
180 while (r.NodeType != XmlNodeType.Element) {
182 throw new Exception ("Unable to read XML file: " +
185 if (r.LocalName == "doc") {
186 slashdocs = new XmlDocument();
187 slashdocs.Load (opts.import);
189 else if (r.LocalName == "Libraries") {
190 ecmadocs = new XmlTextReader (opts.import);
193 throw new Exception ("Unsupported XML format within " + opts.import);
196 } catch (Exception e) {
197 Error ("Could not load XML file: {0}", e.Message);
198 Environment.ExitCode = 1;
203 // PERFORM THE UPDATES
205 string dest_dir = opts.updateto != null ? opts.updateto : opts.path;
206 if (opts.type != null && opts.type.Length > 0)
207 DoUpdateTypes(opts.path, opts.type, dest_dir);
208 else if (opts.@namespace != null)
209 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
210 Path.Combine (dest_dir, opts.@namespace));
212 DoUpdateAssemblies(opts.path, dest_dir);
214 } catch (InvalidOperationException error) {
215 Error (opts.show_exceptions ? error.ToString () : error.Message);
216 Environment.ExitCode = 1;
219 } catch (System.IO.IOException error) {
220 Error (opts.show_exceptions ? error.ToString () : error.Message);
221 Environment.ExitCode = 1;
224 } catch (Exception error) {
225 Error (opts.show_exceptions ? error.ToString () : error.Message);
226 Environment.ExitCode = 1;
229 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
232 private static void Error (object message)
234 Console.Error.Write ("monodocer: ");
235 Console.Error.WriteLine (message);
238 private static void Error (string format, params object[] args)
240 Console.Error.Write ("monodocer: ");
241 Console.Error.WriteLine (format, args);
244 private static Assembly LoadAssembly (string name)
246 Assembly assembly = null;
248 assembly = Assembly.LoadFile (name);
249 } catch (System.IO.FileNotFoundException) { }
251 if (assembly == null) {
253 assembly = Assembly.LoadWithPartialName (name);
254 } catch (Exception) { }
257 if (assembly == null)
258 throw new InvalidOperationException("Assembly " + name + " not found.");
263 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
264 OrderTypeAttributes (element);
265 XmlTextWriter writer = new XmlTextWriter(output);
266 writer.Formatting = Formatting.Indented;
267 writer.Indentation = 2;
268 writer.IndentChar = ' ';
269 element.WriteTo(writer);
273 private static void OrderTypeAttributes (XmlElement e)
275 foreach (XmlElement type in e.SelectNodes ("//Type")) {
276 OrderTypeAttributes (type.Attributes);
280 static readonly string[] TypeAttributeOrder = {
281 "Name", "FullName", "FullNameSP", "Maintainer"
284 private static void OrderTypeAttributes (XmlAttributeCollection c)
286 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
287 for (int i = 0; i < c.Count; ++i) {
288 XmlAttribute a = c [i];
289 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
290 if (a.Name == TypeAttributeOrder [j]) {
296 for (int i = attrs.Length-1; i >= 0; --i) {
297 XmlAttribute n = attrs [i];
300 XmlAttribute r = null;
301 for (int j = i+1; j < attrs.Length; ++j) {
302 if (attrs [j] != null) {
310 c.InsertBefore (n, r);
314 private static XmlDocument CreateIndexStub() {
315 XmlDocument index = new XmlDocument();
317 XmlElement index_root = index.CreateElement("Overview");
318 index.AppendChild(index_root);
320 if (assemblies.Length == 0)
321 throw new Exception ("No assembly");
323 XmlElement index_assemblies = index.CreateElement("Assemblies");
324 index_root.AppendChild(index_assemblies);
326 XmlElement index_remarks = index.CreateElement("Remarks");
327 index_remarks.InnerText = "To be added.";
328 index_root.AppendChild(index_remarks);
330 XmlElement index_copyright = index.CreateElement("Copyright");
331 index_copyright.InnerText = "To be added.";
332 index_root.AppendChild(index_copyright);
334 XmlElement index_types = index.CreateElement("Types");
335 index_root.AppendChild(index_types);
340 private static void WriteNamespaceStub(string ns, string outdir) {
341 XmlDocument index = new XmlDocument();
343 XmlElement index_root = index.CreateElement("Namespace");
344 index.AppendChild(index_root);
346 index_root.SetAttribute("Name", ns);
348 XmlElement index_docs = index.CreateElement("Docs");
349 index_root.AppendChild(index_docs);
351 XmlElement index_summary = index.CreateElement("summary");
352 index_summary.InnerText = "To be added.";
353 index_docs.AppendChild(index_summary);
355 XmlElement index_remarks = index.CreateElement("remarks");
356 index_remarks.InnerText = "To be added.";
357 index_docs.AppendChild(index_remarks);
359 using (TextWriter writer = OpenWrite (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew)) {
360 WriteXml(index.DocumentElement, writer);
364 public static void DoUpdateTypes(string basepath, string[] typenames, string dest) {
365 ArrayList found = new ArrayList ();
366 foreach (Assembly assembly in assemblies) {
367 foreach (DocsTypeInfo docsTypeInfo in GetTypes (assembly, typenames)) {
368 string relpath = DoUpdateType (docsTypeInfo.Type, basepath, dest, docsTypeInfo.EcmaDocs);
370 found.Add (docsTypeInfo.Type.FullName);
373 StringList notFound = new StringList (typenames.Length);
374 foreach (string typename in typenames)
375 if (!found.Contains (typename))
376 notFound.Add (typename);
377 if (notFound.Count > 0)
378 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", DocUtils.ToStringArray (notFound)));
381 public static string DoUpdateType(Type type, string basepath, string dest, XmlReader ecmaDocsType)
383 if (type.Namespace == null)
384 Error ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
386 if (!IsPublic (type))
389 // Must get the A+B form of the type name.
390 string typename = GetTypeFileName(type);
392 string reltypefile = DocUtils.PathCombine (type.Namespace, typename + ".xml");
393 string typefile = Path.Combine (basepath, reltypefile);
394 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
396 string output = null;
399 } else if (dest == "-") {
402 output = Path.Combine (dest, reltypefile);
407 XmlDocument basefile = new XmlDocument();
408 if (!pretty) basefile.PreserveWhitespace = true;
410 basefile.Load(typefile);
411 } catch (Exception e) {
412 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
415 DoUpdateType2("Updating", basefile, type, output, false, ecmaDocsType);
418 XmlElement td = StubType(type, output, ecmaDocsType);
422 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
425 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
431 private static XPathNavigator SelectSingleNode (XPathNavigator n, string xpath)
434 return n.SelectSingleNode (xpath);
436 XPathNodeIterator i = n.Select (xpath);
437 XPathNavigator r = null;
438 while (i.MoveNext ()) {
445 public static void DoUpdateNS(string ns, string nspath, string outpath) {
446 Hashtable seenTypes = new Hashtable();
447 Assembly assembly = assemblies [0];
449 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
450 XmlDocument basefile = new XmlDocument();
451 if (!pretty) basefile.PreserveWhitespace = true;
452 string typefile = Path.Combine(nspath, file.Name);
454 basefile.Load(typefile);
455 } catch (Exception e) {
456 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
460 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
461 Type type = assembly.GetType(typename, false);
463 Error ("Type no longer in assembly: " + typename);
467 seenTypes[type] = seenTypes;
468 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false, null);
471 // Stub types not in the directory
472 foreach (DocsTypeInfo docsTypeInfo in GetTypes (assembly, null)) {
473 Type type = docsTypeInfo.Type;
474 if (type.Namespace != ns || seenTypes.ContainsKey(type))
477 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"), docsTypeInfo.EcmaDocs);
478 if (td == null) continue;
482 private static string GetTypeFileName(Type type) {
483 return filenameFormatter.GetName (type);
486 public static string GetTypeFileName (string typename)
488 StringBuilder filename = new StringBuilder (typename.Length);
492 for (int i = 0; i < typename.Length; ++i) {
493 char c = typename [i];
502 filename.Append ('`').Append ((numArgs+1).ToString());
517 return filename.ToString ();
521 private static void AddIndexAssembly (Assembly assembly, XmlElement parent)
523 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
524 index_assembly.SetAttribute("Name", assembly.GetName().Name);
525 index_assembly.SetAttribute("Version", assembly.GetName().Version.ToString());
526 MakeAttributes(index_assembly, assembly, true);
527 parent.AppendChild(index_assembly);
530 private static void DoUpdateAssemblies (string source, string dest)
532 string indexfile = dest + "/index.xml";
534 if (System.IO.File.Exists(indexfile)) {
535 index = new XmlDocument();
536 index.Load(indexfile);
539 ClearElement(index.DocumentElement, "Assembly");
540 ClearElement(index.DocumentElement, "Attributes");
542 index = CreateIndexStub();
546 string defaultTitle = "Untitled";
547 if (assemblies.Length == 1)
548 defaultTitle = assemblies[0].GetName().Name;
549 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
551 WriteElementText(index.DocumentElement, "Title", name);
554 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
555 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
556 index_assemblies.RemoveAll ();
558 Hashtable goodfiles = new Hashtable();
560 foreach (Assembly assm in assemblies) {
561 AddIndexAssembly (assm, index_assemblies);
562 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
565 SortIndexEntries (index_types);
567 CleanupFiles (dest, goodfiles);
568 CleanupIndexTypes (index_types, goodfiles);
569 CleanupExtensions (index_types);
571 using (TextWriter writer = OpenWrite (indexfile, FileMode.Create))
572 WriteXml(index.DocumentElement, writer);
575 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
577 private static void DoUpdateAssembly (Assembly assembly, XmlElement index_types, string source, string dest, Hashtable goodfiles)
579 foreach (DocsTypeInfo docTypeInfo in GetTypes (assembly, null)) {
580 Type type = docTypeInfo.Type;
581 if (!IsPublic (type) || type.FullName.IndexOfAny (InvalidFilenameChars) >= 0)
584 string reltypepath = DoUpdateType (type, source, dest, docTypeInfo.EcmaDocs);
585 if (reltypepath == null)
588 // Add namespace and type nodes into the index file as needed
589 XmlElement nsnode = (XmlElement)index_types.SelectSingleNode("Namespace[@Name='" + type.Namespace + "']");
590 if (nsnode == null) {
591 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
592 nsnode.SetAttribute("Name", type.Namespace);
593 index_types.AppendChild(nsnode);
595 string typename = GetTypeFileName(type);
596 string doc_typename = GetDocTypeName (type);
597 XmlElement typenode = (XmlElement)nsnode.SelectSingleNode("Type[@Name='" + typename + "']");
598 if (typenode == null) {
599 typenode = index_types.OwnerDocument.CreateElement("Type");
600 typenode.SetAttribute("Name", typename);
601 nsnode.AppendChild(typenode);
603 if (typename != doc_typename)
604 typenode.SetAttribute("DisplayName", doc_typename);
606 typenode.RemoveAttribute("DisplayName");
607 typenode.SetAttribute ("Kind", GetTypeKind (type));
609 // Ensure the namespace index file exists
610 string onsdoc = DocUtils.PathCombine (dest, type.Namespace + ".xml");
611 string nsdoc = DocUtils.PathCombine (dest, "ns-" + type.Namespace + ".xml");
612 if (File.Exists (onsdoc)) {
613 File.Move (onsdoc, nsdoc);
616 if (!File.Exists (nsdoc)) {
617 Console.WriteLine("New Namespace File: " + type.Namespace);
618 WriteNamespaceStub(type.Namespace, dest);
621 // mark the file as corresponding to a type
622 goodfiles[reltypepath] = goodfiles;
628 public XmlReader EcmaDocs;
630 public DocsTypeInfo (Type type, XmlReader docs)
633 this.EcmaDocs = docs;
641 IEnumerable<Mono.Documentation.Updater.DocsTypeInfo>
643 GetTypes (Assembly assembly, string[] forTypes)
645 Hashtable seen = null;
646 if (forTypes != null)
647 Array.Sort (forTypes);
648 if (ecmadocs != null) {
649 seen = new Hashtable ();
651 while (ecmadocs.Read ()) {
652 switch (ecmadocs.Name) {
655 typeDepth = ecmadocs.Depth;
656 if (ecmadocs.NodeType != XmlNodeType.Element)
658 if (typeDepth != ecmadocs.Depth) // nested <Type/> element?
660 string typename = ecmadocs.GetAttribute ("FullName");
661 string typename2 = GetTypeFileName (typename);
662 if (forTypes != null &&
663 Array.BinarySearch (forTypes, typename) < 0 &&
664 typename != typename2 &&
665 Array.BinarySearch (forTypes, typename2) < 0)
668 if ((t = assembly.GetType (typename, false)) == null &&
669 (t = assembly.GetType (typename2, false)) == null)
671 seen.Add (typename, "");
672 if (typename != typename2)
673 seen.Add (typename2, "");
674 Console.WriteLine (" Import: {0}", t.FullName);
675 yield return new DocsTypeInfo (t, ecmadocs);
683 foreach (Type type in assembly.GetTypes()) {
684 if (forTypes != null && Array.BinarySearch (forTypes, type.FullName) < 0)
686 if (seen != null && seen.ContainsKey (type.FullName))
688 yield return new DocsTypeInfo (type, null);
692 private static void SortIndexEntries (XmlElement indexTypes)
694 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
695 XmlNodeComparer c = new AttributeNameComparer ();
696 SortXmlNodes (indexTypes, namespaces, c);
698 for (int i = 0; i < namespaces.Count; ++i)
699 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
702 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
704 MyXmlNodeList l = new MyXmlNodeList (children.Count);
705 for (int i = 0; i < children.Count; ++i)
706 l.Add (children [i]);
708 for (int i = l.Count - 1; i > 0; --i) {
709 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
713 abstract class XmlNodeComparer : IComparer
718 public abstract int Compare (XmlNode x, XmlNode y);
720 public int Compare (object x, object y)
722 return Compare ((XmlNode) x, (XmlNode) y);
726 class AttributeNameComparer : XmlNodeComparer {
727 public override int Compare (XmlNode x, XmlNode y)
729 return x.Attributes ["Name"].Value.CompareTo (y.Attributes ["Name"].Value);
733 class VersionComparer : XmlNodeComparer {
734 public override int Compare (XmlNode x, XmlNode y)
736 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
737 string a = GetVersion (x.InnerText);
738 string b = GetVersion (y.InnerText);
739 return new Version (a).CompareTo (new Version (b));
742 static string GetVersion (string v)
744 int n = v.IndexOf ("x");
747 return v.Substring (0, n-1);
751 private static string GetTypeKind (Type type)
754 return "Enumeration";
755 if (type.IsValueType)
757 if (type.IsInterface)
759 if (IsDelegate (type))
761 if (type.IsClass || type == typeof(System.Enum))
763 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
766 private static bool IsPublic (Type type)
769 while (decl != null) {
770 if (!(decl.IsPublic || decl.IsNestedPublic)) {
773 decl = decl.DeclaringType;
778 private static void CleanupFiles (string dest, Hashtable goodfiles)
780 // Look for files that no longer correspond to types
781 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
782 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
783 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
784 if (!goodfiles.ContainsKey(relTypeFile)) {
785 XmlDocument doc = new XmlDocument ();
786 doc.Load (typefile.FullName);
787 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
788 if (UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
789 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
790 WriteXml(doc.DocumentElement, writer);
791 goodfiles [relTypeFile] = goodfiles;
794 string newname = typefile.FullName + ".remove";
795 try { System.IO.File.Delete(newname); } catch (Exception) { }
796 try { typefile.MoveTo(newname); } catch (Exception) { }
797 Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
803 private static TextWriter OpenWrite (string path, FileMode mode)
805 return new StreamWriter (
806 new FileStream (path, mode),
807 new UTF8Encoding (false)
811 private static string[] GetAssemblyVersions ()
813 StringList versions = new StringList (assemblies.Length);
814 for (int i = 0; i < assemblies.Length; ++i)
815 versions.Add (GetAssemblyVersion (assemblies [i]));
816 return DocUtils.ToStringArray (versions);
819 private static void CleanupIndexTypes (XmlElement index_types, Hashtable goodfiles)
821 // Look for type nodes that no longer correspond to types
822 MyXmlNodeList remove = new MyXmlNodeList ();
823 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
824 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
825 if (!goodfiles.ContainsKey(fulltypename)) {
826 remove.Add (typenode);
829 foreach (XmlNode n in remove)
830 n.ParentNode.RemoveChild (n);
833 private static void CleanupExtensions (XmlElement index_types)
835 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
836 if (extensionMethods.Count == 0) {
839 index_types.RemoveChild (e);
843 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
844 index_types.SelectSingleNode ("/Overview").AppendChild (e);
848 extensionMethods.Sort (DefaultExtensionMethodComparer);
849 foreach (XmlNode m in extensionMethods) {
850 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
854 class ExtensionMethodComparer : XmlNodeComparer {
855 public override int Compare (XmlNode x, XmlNode y)
857 XmlNode xLink = x.SelectSingleNode ("Member/Link");
858 XmlNode yLink = y.SelectSingleNode ("Member/Link");
860 int n = xLink.Attributes ["Type"].Value.CompareTo (
861 yLink.Attributes ["Type"].Value);
864 n = xLink.Attributes ["Member"].Value.CompareTo (
865 yLink.Attributes ["Member"].Value);
866 if (n == 0 && !object.ReferenceEquals (x, y))
867 throw new InvalidOperationException ("Duplicate extension method found!");
872 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
874 public static void DoUpdateType2(string message, XmlDocument basefile, Type type, string output, bool insertSince, XmlReader ecmaDocsType) {
875 Console.WriteLine(message + ": " + type.FullName);
877 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
879 // Update type metadata
880 UpdateType(basefile.DocumentElement, type, ecmaDocsType);
882 if (ecmaDocsType != null) {
883 while (ecmaDocsType.Name != "Members" && ecmaDocsType.Read ()) {
886 if (ecmaDocsType.IsEmptyElement)
890 // Update existing members. Delete member nodes that no longer should be there,
891 // and remember what members are already documented so we don't add them again.
892 if (!ignoremembers) {
893 MyXmlNodeList todelete = new MyXmlNodeList ();
894 foreach (DocsNodeInfo info in GetDocumentationMembers (basefile, type, ecmaDocsType)) {
895 XmlElement oldmember = info.Node;
896 MemberInfo oldmember2 = info.Member;
897 string sig = oldmember2 != null ? MakeMemberSignature(oldmember2) : null;
899 // Interface implementations and overrides are deleted from the docs
900 // unless the overrides option is given.
901 if (oldmember2 != null && (!IsNew(oldmember2) || sig == null))
904 // Deleted (or signature changed)
905 if (oldmember2 == null) {
906 if (UpdateAssemblyVersions (oldmember, new string[]{GetAssemblyVersion (type.Assembly)}, false))
908 DeleteMember ("Member Removed", output, oldmember, todelete);
913 if (seenmembers.ContainsKey (sig)) {
914 if (object.ReferenceEquals (oldmember, seenmembers [sig]))
915 ; // ignore, already seen
916 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
917 DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
919 Error ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
923 // Update signature information
926 seenmembers.Add (sig, oldmember);
928 foreach (XmlElement oldmember in todelete)
929 oldmember.ParentNode.RemoveChild (oldmember);
932 if (!IsDelegate(type) && !ignoremembers) {
933 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
934 foreach (MemberInfo m in Sort (type.GetMembers(DefaultBindingFlags))) {
935 if (m is Type) continue;
937 string sig = MakeMemberSignature(m);
938 if (sig == null) continue;
939 if (seenmembers.ContainsKey(sig)) continue;
941 // To be nice on diffs, members/properties/events that are overrides or are interface implementations
943 if (!IsNew(m)) continue;
945 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
946 if (mm == null) continue;
947 members.AppendChild( mm );
949 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
954 // Import code snippets from files
955 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
956 if (!(code is XmlElement)) continue;
957 string file = ((XmlElement)code).GetAttribute("src");
958 string lang = ((XmlElement)code).GetAttribute("lang");
960 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
962 code.InnerText = src;
966 if (insertSince && since != null) {
967 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
968 docs.AppendChild (CreateSinceNode (basefile));
972 XmlElement d = basefile.DocumentElement ["Docs"];
973 XmlElement m = basefile.DocumentElement ["Members"];
974 if (d != null && m != null)
975 basefile.DocumentElement.InsertBefore (
976 basefile.DocumentElement.RemoveChild (d), m);
980 System.IO.TextWriter writer;
982 writer = Console.Out;
984 FileInfo file = new FileInfo (output);
985 if (!file.Directory.Exists) {
986 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
987 file.Directory.Create ();
989 writer = OpenWrite (output, FileMode.Create);
993 WriteXml(basefile.DocumentElement, writer);
996 private static string GetCodeSource (string lang, string file)
999 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
1000 // Grab the specified region
1001 string region = "#region " + file.Substring (anchorStart + 4);
1002 file = file.Substring (0, anchorStart + 3);
1004 using (StreamReader reader = new StreamReader (file)) {
1006 StringBuilder src = new StringBuilder ();
1008 while ((line = reader.ReadLine ()) != null) {
1009 if (line.Trim() == region) {
1010 indent = line.IndexOf (region);
1013 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
1018 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
1021 return src.ToString ();
1023 } catch (Exception e) {
1024 Error ("Could not load <code/> file '{0}' region '{1}': {2}",
1025 file, region, show_exceptions ? e.ToString () : e.Message);
1030 using (StreamReader reader = new StreamReader (file))
1031 return reader.ReadToEnd ();
1032 } catch (Exception e) {
1033 Error ("Could not load <code/> file '" + file + "': " + e.Message);
1042 IEnumerable<DocsNodeInfo>
1044 GetDocumentationMembers (XmlDocument basefile, Type type, XmlReader ecmaDocsMembers)
1046 if (ecmaDocsMembers != null) {
1047 int membersDepth = ecmaDocsMembers.Depth;
1049 while (go && ecmaDocsMembers.Read ()) {
1050 switch (ecmaDocsMembers.Name) {
1052 if (membersDepth != ecmaDocsMembers.Depth - 1 || ecmaDocsMembers.NodeType != XmlNodeType.Element)
1054 DocumentationMember dm = new DocumentationMember (ecmaDocsMembers);
1055 string xp = GetXPathForMember (dm);
1056 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
1058 if (oldmember == null) {
1059 m = GetMember (type, dm);
1061 Error ("Could not import ECMA docs for `{0}'s `{1}': MemberInfo not found.",
1062 type.FullName, dm.MemberSignatures ["C#"]);
1063 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
1066 // oldmember lookup may have failed due to type parameter renames.
1068 oldmember = (XmlElement) basefile.SelectSingleNode (GetXPathForMember (m));
1069 if (oldmember == null) {
1070 XmlElement members = WriteElement(basefile.DocumentElement, "Members");
1071 oldmember = basefile.CreateElement ("Member");
1072 oldmember.SetAttribute ("MemberName", dm.MemberName);
1073 members.AppendChild (oldmember);
1074 foreach (string key in Sort (dm.MemberSignatures.Keys)) {
1075 XmlElement ms = basefile.CreateElement ("MemberSignature");
1076 ms.SetAttribute ("Language", key);
1077 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
1078 oldmember.AppendChild (ms);
1080 oldmember.SetAttribute ("__monodocer-seen__", "true");
1081 Console.WriteLine ("Member Added: {0}", MakeMemberSignature (m));
1086 m = GetMember (type, new DocumentationMember (oldmember));
1088 Error ("Could not import ECMA docs for `{0}'s `{1}': MemberInfo not found.",
1089 type.FullName, dm.MemberSignatures ["C#"]);
1092 oldmember.SetAttribute ("__monodocer-seen__", "true");
1094 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
1095 if (ecmaDocsMembers.Name != "Docs")
1096 throw new InvalidOperationException ("Found " + ecmaDocsMembers.Name + "; expected <Docs/>!");
1097 node.EcmaDocs = ecmaDocsMembers;
1102 if (membersDepth == ecmaDocsMembers.Depth && ecmaDocsMembers.NodeType == XmlNodeType.EndElement) {
1109 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
1110 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
1111 oldmember.RemoveAttribute ("__monodocer-seen__");
1114 MemberInfo m = GetMember (type, new DocumentationMember (oldmember));
1116 yield return new DocsNodeInfo (oldmember);
1119 yield return new DocsNodeInfo (oldmember, m);
1124 static void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
1126 string format = output != null
1127 ? "{0}: File='{1}'; Signature='{4}'"
1128 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1132 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1133 member.Attributes ["MemberName"].Value,
1134 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
1135 if (!delete && MemberDocsHaveUserContent (member)) {
1136 Error ("Member deletions must be enabled with the --delete option.");
1138 todelete.Add (member);
1143 class MemberComparer : XmlNodeComparer {
1144 public override int Compare (XmlNode x, XmlNode y)
1147 string xMemberName = x.Attributes ["MemberName"].Value;
1148 string yMemberName = y.Attributes ["MemberName"].Value;
1150 // generic methods *end* with '>'
1151 // it's possible for explicitly implemented generic interfaces to
1152 // contain <...> without being a generic method
1153 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1154 (r = xMemberName.CompareTo (yMemberName)) != 0)
1158 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1159 xMemberName = xMemberName.Substring (0, lt);
1160 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1161 yMemberName = yMemberName.Substring (0, lt);
1162 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1165 // if @MemberName matches, then it's either two different types of
1166 // members sharing the same name, e.g. field & property, or it's an
1167 // overloaded method.
1168 // for different type, sort based on MemberType value.
1169 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1170 y.SelectSingleNode ("MemberType").InnerText);
1174 // same type -- must be an overloaded method. Sort based on type
1175 // parameter count, then parameter count, then by the parameter
1177 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1178 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1179 if (xTypeParams.Count != yTypeParams.Count)
1180 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1181 for (int i = 0; i < xTypeParams.Count; ++i) {
1182 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1183 yTypeParams [i].Attributes ["Name"].Value);
1188 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1189 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1190 if (xParams.Count != yParams.Count)
1191 return xParams.Count <= yParams.Count ? -1 : 1;
1192 for (int i = 0; i < xParams.Count; ++i) {
1193 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1194 yParams [i].Attributes ["Type"].Value);
1198 // all parameters match, but return value might not match if it was
1199 // changed between one version and another.
1200 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1201 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1202 if (xReturn != null && yReturn != null) {
1203 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1212 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1214 private static void SortTypeMembers (XmlNode members)
1216 if (members == null)
1218 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1221 private static bool MemberDocsHaveUserContent (XmlNode e)
1223 e = (XmlElement)e.SelectSingleNode("Docs");
1224 if (e == null) return false;
1225 foreach (XmlElement d in e.SelectNodes("*"))
1226 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1231 private static bool IsNew(MemberInfo m) {
1232 if (!nooverrides) return true;
1233 if (m is MethodInfo && !IsNew((MethodInfo)m)) return false;
1234 if (m is PropertyInfo && !IsNew(((PropertyInfo)m).GetGetMethod())) return false;
1235 if (m is PropertyInfo && !IsNew(((PropertyInfo)m).GetSetMethod())) return false;
1236 if (m is EventInfo && !IsNew(((EventInfo)m).GetAddMethod(true))) return false;
1237 if (m is EventInfo && !IsNew(((EventInfo)m).GetRaiseMethod())) return false;
1238 if (m is EventInfo && !IsNew(((EventInfo)m).GetRemoveMethod())) return false;
1242 private static bool IsNew(MethodInfo m) {
1243 if (m == null) return true;
1244 MethodInfo b = m.GetBaseDefinition();
1245 if (b == null || b == m) return true;
1249 // UPDATE HELPER FUNCTIONS
1252 private static XmlElement FindMatchingMember(Type type, XmlElement newfile, XmlElement oldmember) {
1253 MemberInfo oldmember2 = GetMember(type, oldmember.CreateNavigator ());
1254 if (oldmember2 == null) return null;
1256 string membername = oldmember.GetAttribute("MemberName");
1257 foreach (XmlElement newmember in newfile.SelectNodes("Members/Member[@MemberName='" + membername + "']")) {
1258 if (GetMember(type, newmember.CreateNavigator ()) == oldmember2) return newmember;
1265 private static MemberInfo GetMember(Type type, DocumentationMember member) {
1266 string membertype = member.MemberType;
1268 string returntype = member.ReturnType;
1270 string docName = member.MemberName;
1271 string[] docTypeParams = GetTypeParameters (docName);
1273 // Loop through all members in this type with the same name
1274 foreach (MemberInfo mi in GetReflectionMembers (type, docName)) {
1275 if (mi is Type) continue;
1276 if (GetMemberType(mi) != membertype) continue;
1278 string sig = MakeMemberSignature(mi);
1279 if (sig == null) continue; // not publicly visible
1281 ParameterInfo[] pis = null;
1282 string[] typeParams = null;
1283 if (mi is MethodInfo || mi is ConstructorInfo) {
1284 MethodBase mb = (MethodBase) mi;
1285 pis = mb.GetParameters();
1286 if (docTypeParams != null && DocUtils.GetContainsGenericParameters (mb)) {
1287 Type[] args = DocUtils.GetGenericArguments (mb);
1288 if (args.Length == docTypeParams.Length) {
1289 typeParams = new string [args.Length];
1290 for (int i = 0; i < args.Length; ++i)
1291 typeParams [i] = args [i].Name;
1295 else if (mi is PropertyInfo)
1296 pis = ((PropertyInfo)mi).GetIndexParameters();
1298 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
1299 int pcount = pis == null ? 0 : pis.Length;
1300 if (mcount != pcount)
1303 if (mi is MethodInfo) {
1304 // Casting operators can overload based on return type.
1305 if (returntype != GetReplacedString (
1306 GetDocTypeFullName (((MethodInfo)mi).ReturnType),
1307 typeParams, docTypeParams)) {
1315 for (int i = 0; i < pis.Length; i++) {
1316 string paramType = GetReplacedString (
1317 GetDocParameterType (pis [i].ParameterType),
1318 typeParams, docTypeParams);
1319 if (paramType != (string) member.Parameters [i]) {
1324 if (!good) continue;
1332 private static MemberInfoEnumerable GetReflectionMembers (Type type, string docName)
1334 // need to worry about 4 forms of //@MemberName values:
1335 // 1. "Normal" (non-generic) member names: GetEnumerator
1337 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
1338 // - try as-is, and try type.member (due to "kludge" for property
1340 // 3. "Normal" Generic member names: Sort<T> (CSC)
1341 // - need to remove generic parameters --> "Sort"
1342 // 4. Explicitly-implemented interface members for generic interfaces:
1343 // -- System.Collections.Generic.IEnumerable<T>.Current
1344 // - Try as-is, and try type.member, *keeping* the generic parameters.
1345 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
1346 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
1347 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
1348 // this as (1) or (2).
1349 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
1351 foreach (MemberInfo mi in type.GetMember (docName, DefaultBindingFlags))
1353 if (CountChars (docName, '.') > 0)
1354 // might be a property; try only type.member instead of
1355 // namespace.type.member.
1356 foreach (MemberInfo mi in
1357 type.GetMember (DocUtils.GetTypeDotMember (docName), DefaultBindingFlags))
1364 int startLt, startType, startMethod;
1365 startLt = startType = startMethod = -1;
1366 for (int i = 0; i < docName.Length; ++i) {
1367 switch (docName [i]) {
1376 if (numLt == 0 && (i + 1) < docName.Length)
1377 // there's another character in docName, so this <...> sequence is
1378 // probably part of a generic type -- case 4.
1382 startType = startMethod;
1388 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
1390 foreach (MemberInfo mi in type.GetMember (refName, DefaultBindingFlags))
1394 foreach (MemberInfo mi in type.GetMember (refName.Substring (startType + 1), DefaultBindingFlags))
1397 // If we _still_ haven't found it, we've hit another generic naming issue:
1398 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
1399 // explicitly-implemented METHOD names (not properties), e.g.
1400 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
1401 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
1402 // which the XML docs will contain.
1404 // Alas, we can't derive the Mono name from docName, so we need to iterate
1405 // over all member names, convert them into CSC format, and compare... :-(
1408 foreach (MemberInfo mi in type.GetMembers (DefaultBindingFlags)) {
1409 if (GetMemberName (mi) == docName)
1414 static string[] GetTypeParameters (string docName)
1416 if (docName [docName.Length-1] != '>')
1418 StringList types = new StringList ();
1419 int endToken = docName.Length-2;
1420 int i = docName.Length-2;
1422 if (docName [i] == ',' || docName [i] == '<') {
1423 types.Add (docName.Substring (i + 1, endToken - i));
1426 if (docName [i] == '<')
1431 return DocUtils.ToStringArray (types);
1434 static string GetReplacedString (string typeName, string[] from, string[] to)
1438 for (int i = 0; i < from.Length; ++i)
1439 typeName = typeName.Replace (from [i], to [i]);
1443 // CREATE A STUB DOCUMENTATION FILE
1445 public static XmlElement StubType(Type type, string output, XmlReader ecmaDocsType) {
1446 string typesig = MakeTypeSignature(type);
1447 if (typesig == null) return null; // not publicly visible
1449 XmlDocument doc = new XmlDocument();
1450 XmlElement root = doc.CreateElement("Type");
1451 doc.AppendChild (root);
1453 DoUpdateType2 ("New Type", doc, type, output, true, ecmaDocsType);
1458 private static XmlElement CreateSinceNode (XmlDocument doc)
1460 XmlElement s = doc.CreateElement ("since");
1461 s.SetAttribute ("version", since);
1465 // STUBBING/UPDATING FUNCTIONS
1467 public static void UpdateType(XmlElement root, Type type, XmlReader ecmaDocsType) {
1468 root.SetAttribute("Name", GetDocTypeName (type));
1469 root.SetAttribute("FullName", GetDocTypeFullName (type));
1471 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Language", "C#");
1472 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Value", MakeTypeSignature(type));
1474 XmlElement ass = WriteElement(root, "AssemblyInfo");
1475 WriteElementText(ass, "AssemblyName", type.Assembly.GetName().Name);
1476 UpdateAssemblyVersions(root, type, true);
1477 if (type.Assembly.GetName().CultureInfo.Name != "")
1478 WriteElementText(ass, "AssemblyCulture", type.Assembly.GetName().CultureInfo.Name);
1480 ClearElement(ass, "AssemblyCulture");
1482 // Why-oh-why do we put assembly attributes in each type file?
1483 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1484 // since they're outdated in current docs, and a waste of space.
1485 //MakeAttributes(ass, type.Assembly, true);
1486 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1487 if (assattrs != null)
1488 ass.RemoveChild(assattrs);
1490 NormalizeWhitespace(ass);
1492 if (DocUtils.IsGenericType (type)) {
1493 MakeTypeParameters (root, DocUtils.GetGenericArguments (type));
1495 ClearElement(root, "TypeParameters");
1498 if (type.BaseType != null) {
1499 XmlElement basenode = WriteElement(root, "Base");
1501 string basetypename = GetDocTypeFullName (type.BaseType);
1502 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1503 WriteElementText(root, "Base/BaseTypeName", basetypename);
1505 // Document how this type instantiates the generic parameters of its base type
1506 if (DocUtils.IsGenericType (type.BaseType)) {
1507 ClearElement(basenode, "BaseTypeArguments");
1508 Type[] baseGenArgs = DocUtils.GetGenericArguments (type.BaseType);
1509 Type genericDefinition = DocUtils.GetGenericTypeDefinition (type.BaseType);
1510 Type[] genTypeDefArgs = DocUtils.GetGenericArguments (genericDefinition);
1511 for (int i = 0; i < baseGenArgs.Length; i++) {
1512 Type typearg = baseGenArgs [i];
1513 Type typeparam = genTypeDefArgs [i];
1515 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1516 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1517 bta.AppendChild(arg);
1518 arg.SetAttribute("TypeParamName", typeparam.Name);
1519 arg.InnerText = GetDocTypeFullName (typearg);
1523 ClearElement(root, "Base");
1526 if (!IsDelegate(type) && !type.IsEnum) {
1527 // Get a sorted list of interface implementations. Don't include
1528 // interfaces that are implemented by a base type or another interface
1529 // because they go on the base type or base interface's signature.
1530 ArrayList interface_names = new ArrayList();
1531 foreach (Type i in type.GetInterfaces())
1532 if ((type.BaseType == null || Array.IndexOf(type.BaseType.GetInterfaces(), i) == -1) && InterfaceNotFromAnother(i, type.GetInterfaces()))
1533 interface_names.Add(GetDocTypeFullName (i));
1534 interface_names.Sort();
1536 XmlElement interfaces = WriteElement(root, "Interfaces");
1537 interfaces.RemoveAll();
1538 foreach (string iname in interface_names) {
1539 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1540 interfaces.AppendChild(iface);
1541 WriteElementText(iface, "InterfaceName", iname);
1544 ClearElement(root, "Interfaces");
1547 MakeAttributes(root, type, false);
1549 if (IsDelegate(type)) {
1550 MakeTypeParameters (root, DocUtils.GetGenericArguments (type));
1551 MakeParameters(root, type.GetMethod("Invoke").GetParameters());
1552 MakeReturnValue(root, type.GetMethod("Invoke"));
1555 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1556 if (ecmaDocsType != null) {
1557 if (ecmaDocsType.Name != "Docs") {
1558 int depth = ecmaDocsType.Depth;
1559 while (ecmaDocsType.Read ()) {
1560 if (ecmaDocsType.Name == "Docs" && ecmaDocsType.Depth == depth + 1)
1564 if (!ecmaDocsType.IsStartElement ("Docs"))
1565 throw new InvalidOperationException ("Found " + ecmaDocsType.Name + "; expecting <Docs/>!");
1566 typeInfo.EcmaDocs = ecmaDocsType;
1568 MakeDocNode (typeInfo);
1570 if (!IsDelegate (type))
1571 WriteElement (root, "Members");
1573 NormalizeWhitespace(root);
1576 class MemberInfoComparer : IComparer
1578 , IComparer<MemberInfo>
1581 public int Compare (MemberInfo x, MemberInfo y)
1583 string xs = slashdocFormatter.GetName (x);
1584 string ys = slashdocFormatter.GetName (y);
1585 // return String.Compare (xs, ys, StringComparison.OrdinalIgnoreCase);
1586 return string.Compare (xs, ys, true, CultureInfo.InvariantCulture);
1589 public int Compare (object x, object y)
1591 return Compare ((MemberInfo) x, (MemberInfo) y);
1595 static MemberInfoComparer memberInfoComparer = new MemberInfoComparer ();
1597 private static MemberInfo[] Sort (MemberInfo[] members)
1600 ArrayList l = new ArrayList ();
1601 l.AddRange (members);
1602 l.Sort (memberInfoComparer);
1603 return (MemberInfo[]) l.ToArray (typeof(MemberInfo));
1605 Array.Sort (members, memberInfoComparer);
1610 static IEnumerable Sort (IEnumerable list)
1612 ArrayList l = new ArrayList (list as ICollection);
1618 static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1620 List<T> l = new List<T> (list);
1626 private static void UpdateMember(DocsNodeInfo info) {
1627 XmlElement me = (XmlElement) info.Node;
1628 MemberInfo mi = info.Member;
1629 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Language", "C#");
1630 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Value", MakeMemberSignature(mi));
1632 WriteElementText(me, "MemberType", GetMemberType(mi));
1634 UpdateAssemblyVersions(me, mi, true);
1635 MakeAttributes(me, mi, false);
1636 MakeReturnValue(me, mi);
1637 if (mi is MethodBase) {
1638 MethodBase mb = (MethodBase) mi;
1639 if (DocUtils.GetContainsGenericParameters (mb))
1640 MakeTypeParameters (me, DocUtils.GetGenericArguments (mb));
1642 MakeParameters(me, mi);
1645 if (mi is FieldInfo && GetFieldConstValue((FieldInfo)mi, out fieldValue))
1646 WriteElementText(me, "MemberValue", fieldValue);
1648 info.Node = WriteElement (me, "Docs");
1650 UpdateExtensionMethods (me, info);
1653 static readonly string[] ValidExtensionMembers = {
1662 static readonly string[] ValidExtensionDocMembers = {
1668 private static void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1670 MethodInfo me = info.Member as MethodInfo;
1673 if (info.Parameters.Length < 1)
1675 if (!DocUtils.IsExtensionMethod (me))
1678 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1679 XmlNode member = e.CloneNode (true);
1680 em.AppendChild (member);
1681 RemoveExcept (member, ValidExtensionMembers);
1682 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1683 WriteElementText (member, "MemberType", "ExtensionMethod");
1684 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1685 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1686 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1687 member.AppendChild (link);
1688 AddTargets (em, info);
1690 extensionMethods.Add (em);
1693 private static void RemoveExcept (XmlNode node, string[] except)
1697 MyXmlNodeList remove = null;
1698 foreach (XmlNode n in node.ChildNodes) {
1699 if (Array.BinarySearch (except, n.Name) < 0) {
1701 remove = new MyXmlNodeList ();
1706 foreach (XmlNode n in remove)
1707 node.RemoveChild (n);
1710 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1712 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1713 member.PrependChild (targets);
1714 if (!DocUtils.IsGenericParameter (info.Parameters [0].ParameterType))
1715 AppendElementAttributeText (targets, "Target", "Type",
1716 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1718 Type[] constraints = DocUtils.GetGenericParameterConstraints (
1719 info.Parameters [0].ParameterType);
1720 if (constraints.Length == 0)
1721 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1723 foreach (Type c in constraints)
1724 AppendElementAttributeText(targets, "Target", "Type",
1725 slashdocFormatter.GetDeclaration (c));
1729 private static bool GetFieldConstValue(FieldInfo field, out string value) {
1731 if (field.DeclaringType.IsEnum) return false;
1732 if (DocUtils.IsGenericType (field.DeclaringType)) return false;
1733 if (field.IsLiteral || (field.IsStatic && field.IsInitOnly)) {
1736 val = field.GetValue(null);
1740 if (val == null) value = "null";
1741 else if (val is Enum) value = val.ToString();
1742 else if (val is IFormattable) {
1743 value = ((IFormattable)val).ToString();
1745 value = "\"" + value + "\"";
1747 if (value != null && value != "")
1753 // XML HELPER FUNCTIONS
1755 private static XmlElement WriteElement(XmlNode parent, string element) {
1756 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1758 string[] path = element.Split('/');
1759 foreach (string p in path) {
1760 ret = (XmlElement)parent.SelectSingleNode(p);
1763 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1764 ename = ename.Substring(0, ename.IndexOf('['));
1765 ret = parent.OwnerDocument.CreateElement(ename);
1766 parent.AppendChild(ret);
1775 private static void WriteElementText(XmlNode parent, string element, string value) {
1776 XmlElement node = WriteElement(parent, element);
1777 node.InnerText = value;
1780 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1782 XmlElement n = parent.OwnerDocument.CreateElement (element);
1783 parent.AppendChild (n);
1784 n.InnerText = value;
1788 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1790 XmlElement n = parent.OwnerDocument.CreateElement (element);
1791 parent.AppendChild (n);
1792 n.SetAttribute (attribute, value);
1796 private static XmlNode CopyNode (XmlNode source, XmlNode dest)
1798 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1799 dest.AppendChild (copy);
1803 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1804 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1807 node = WriteElement(parent, element);
1808 node.InnerText = value;
1810 private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
1811 XmlElement node = WriteElement(parent, element);
1812 if (node.GetAttribute(attribute) == value) return;
1813 node.SetAttribute(attribute, value);
1815 private static void ClearElement(XmlElement parent, string name) {
1816 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1818 parent.RemoveChild(node);
1820 private static void ClearElementChildren(XmlElement parent) {
1824 // DOCUMENTATION HELPER FUNCTIONS
1826 private static void MakeDocNode (DocsNodeInfo info)
1828 Type[] genericParams = info.GenericParameters;
1829 ParameterInfo[] parameters = info.Parameters;
1830 Type returntype = info.ReturnType;
1831 bool returnisreturn = info.ReturnIsReturn;
1832 XmlElement e = info.Node;
1833 bool addremarks = info.AddRemarks;
1835 WriteElementInitialText(e, "summary", "To be added.");
1837 if (parameters != null) {
1838 string[] values = new string [parameters.Length];
1839 for (int i = 0; i < values.Length; ++i)
1840 values [i] = parameters [i].Name;
1841 UpdateParameters (e, "param", values);
1844 if (genericParams != null) {
1845 string[] values = new string [genericParams.Length];
1846 for (int i = 0; i < values.Length; ++i)
1847 values [i] = genericParams [i].Name;
1848 UpdateParameters (e, "typeparam", values);
1851 string retnodename = null;
1852 if (returntype != null && returntype != typeof(void)) {
1853 retnodename = returnisreturn ? "returns" : "value";
1854 string retnodename_other = !returnisreturn ? "returns" : "value";
1856 // If it has a returns node instead of a value node, change its name.
1857 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1858 if (retother != null) {
1859 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1860 foreach (XmlNode node in retother)
1861 retnode.AppendChild(node.CloneNode(true));
1862 e.ReplaceChild(retnode, retother);
1864 WriteElementInitialText(e, retnodename, "To be added.");
1867 ClearElement(e, "returns");
1868 ClearElement(e, "value");
1872 WriteElementInitialText(e, "remarks", "To be added.");
1874 if (info.EcmaDocs != null) {
1875 XmlReader r = info.EcmaDocs;
1876 int depth = r.Depth;
1877 r.ReadStartElement ("Docs");
1879 if (r.Name == "Docs") {
1880 if (r.Depth == depth && r.NodeType == XmlNodeType.EndElement)
1883 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
1885 if (!r.IsStartElement ())
1890 XmlNode doc = e.SelectSingleNode (
1891 r.Name + "[@name='" + r.GetAttribute ("name") + "']");
1892 string value = r.ReadInnerXml ();
1894 doc.InnerXml = value.Replace ("\r", "");
1901 string name = r.Name;
1902 string cref = r.GetAttribute ("cref");
1903 XmlNode doc = e.SelectSingleNode (
1904 r.Name + "[@cref='" + cref + "']");
1905 string value = r.ReadInnerXml ().Replace ("\r", "");
1907 doc.InnerXml = value;
1909 XmlElement n = e.OwnerDocument.CreateElement (name);
1910 n.SetAttribute ("cref", cref);
1917 string name = r.Name;
1918 string xpath = r.Name;
1919 StringList attributes = new StringList (r.AttributeCount);
1920 if (r.MoveToFirstAttribute ()) {
1922 attributes.Add ("@" + r.Name + "=\"" + r.Value + "\"");
1923 } while (r.MoveToNextAttribute ());
1926 if (attributes.Count > 0) {
1927 xpath += "[" + string.Join (" and ", DocUtils.ToStringArray (attributes)) + "]";
1929 XmlNode doc = e.SelectSingleNode (xpath);
1930 string value = r.ReadInnerXml ().Replace ("\r", "");
1932 doc.InnerXml = value;
1935 XmlElement n = e.OwnerDocument.CreateElement (name);
1937 foreach (string a in attributes) {
1938 int eq = a.IndexOf ('=');
1939 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
1948 if (info.SlashDocs != null) {
1949 XmlNode elem = info.SlashDocs;
1951 if (elem.SelectSingleNode("summary") != null)
1952 ClearElement(e, "summary");
1953 if (elem.SelectSingleNode("remarks") != null)
1954 ClearElement(e, "remarks");
1955 if (elem.SelectSingleNode("value") != null)
1956 ClearElement(e, "value");
1957 if (retnodename != null && elem.SelectSingleNode(retnodename) != null)
1958 ClearElement(e, retnodename);
1960 foreach (XmlNode child in elem.ChildNodes) {
1961 switch (child.Name) {
1964 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + child.Attributes ["name"].Value + "']");
1966 p2.InnerXml = child.InnerXml;
1971 case "permission": {
1972 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + child.Attributes ["cref"].Value + "']");
1974 a = e.OwnerDocument.CreateElement (child.Name);
1975 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
1978 a.InnerXml = child.InnerXml;
1982 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + child.Attributes ["cref"].Value + "']");
1984 a = e.OwnerDocument.CreateElement ("altmember");
1985 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
1991 CopyNode (child, e);
1998 OrderDocsNodes (e, e.ChildNodes);
1999 NormalizeWhitespace(e);
2002 static readonly string[] DocsNodeOrder = {
2003 "typeparam", "param", "summary", "returns", "value", "remarks",
2006 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
2008 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
2009 for (int i = 0; i < DocsNodeOrder.Length; ++i) {
2010 for (int j = 0; j < children.Count; ++j) {
2011 XmlNode c = children [j];
2012 if (c.Name == DocsNodeOrder [i]) {
2013 newChildren.Add (c);
2017 if (newChildren.Count >= 0)
2018 docs.PrependChild ((XmlNode) newChildren [0]);
2019 for (int i = 1; i < newChildren.Count; ++i) {
2020 XmlNode prev = (XmlNode) newChildren [i-1];
2021 XmlNode cur = (XmlNode) newChildren [i];
2022 docs.RemoveChild (cur);
2023 docs.InsertAfter (cur, prev);
2028 private static void UpdateParameters (XmlElement e, string element, string[] values)
2030 if (values != null) {
2031 XmlNode[] paramnodes = new XmlNode[values.Length];
2033 // Some documentation had param nodes with leading spaces.
2034 foreach (XmlElement paramnode in e.SelectNodes(element)){
2035 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
2038 // If a member has only one parameter, we can track changes to
2039 // the name of the parameter easily.
2040 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
2041 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
2044 bool reinsert = false;
2046 // Pick out existing and still-valid param nodes, and
2047 // create nodes for parameters not in the file.
2048 Hashtable seenParams = new Hashtable();
2049 for (int pi = 0; pi < values.Length; pi++) {
2050 string p = values [pi];
2053 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
2054 if (paramnodes[pi] != null) continue;
2056 XmlElement pe = e.OwnerDocument.CreateElement(element);
2057 pe.SetAttribute("name", p);
2058 pe.InnerText = "To be added.";
2059 paramnodes[pi] = pe;
2063 // Remove parameters that no longer exist and check all params are in the right order.
2065 MyXmlNodeList todelete = new MyXmlNodeList ();
2066 foreach (XmlElement paramnode in e.SelectNodes(element)) {
2067 string name = paramnode.GetAttribute("name");
2068 if (!seenParams.ContainsKey(name)) {
2069 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2070 Error ("The following param node can only be deleted if the --delete option is given: ");
2071 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
2073 Error ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
2074 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2078 Error ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
2079 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2080 e.ParentNode.Attributes ["MemberName"].Value,
2083 Error ("\tValue={0}", paramnode.OuterXml);
2085 todelete.Add (paramnode);
2090 if ((int)seenParams[name] != idx)
2096 foreach (XmlNode n in todelete) {
2097 n.ParentNode.RemoveChild (n);
2100 // Re-insert the parameter nodes at the top of the doc section.
2102 for (int pi = values.Length-1; pi >= 0; pi--)
2103 e.PrependChild(paramnodes[pi]);
2105 // Clear all existing param nodes
2106 foreach (XmlNode paramnode in e.SelectNodes(element)) {
2107 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2108 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
2109 Console.WriteLine(paramnode.OuterXml);
2111 paramnode.ParentNode.RemoveChild(paramnode);
2117 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
2119 string existingName = pe.GetAttribute ("name");
2120 pe.SetAttribute ("name", newName);
2121 if (existingName == newName)
2123 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
2124 if (paramref.GetAttribute ("name").Trim () == existingName)
2125 paramref.SetAttribute ("name", newName);
2128 private static void NormalizeWhitespace(XmlElement e) {
2129 // Remove all text and whitespace nodes from the element so it
2130 // is outputted with nice indentation and no blank lines.
2131 ArrayList deleteNodes = new ArrayList();
2132 foreach (XmlNode n in e)
2133 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2135 foreach (XmlNode n in deleteNodes)
2136 n.ParentNode.RemoveChild(n);
2139 private static bool UpdateAssemblyVersions(XmlElement root, MemberInfo member, bool add)
2141 Type type = member as Type;
2143 type = member.DeclaringType;
2144 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion(type.Assembly) }, add);
2147 private static string GetAssemblyVersion(Assembly assembly)
2149 return assembly.GetName().Version.ToString();
2152 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
2154 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
2156 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2157 root.AppendChild(e);
2159 MyXmlNodeList matches = new MyXmlNodeList (assemblyVersions.Length);
2160 foreach (XmlElement v in e.SelectNodes ("AssemblyVersion")) {
2161 foreach (string sv in assemblyVersions)
2162 if (v.InnerText == sv)
2165 // matches.Count > 0 && add: ignore -- already present
2166 if (matches.Count > 0 && !add) {
2167 foreach (XmlNode c in matches)
2170 else if (matches.Count == 0 && add) {
2171 foreach (string sv in assemblyVersions) {
2172 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2177 // matches.Count == 0 && !add: ignore -- already not present
2179 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2180 SortXmlNodes (e, avs, new VersionComparer ());
2182 return avs.Count != 0;
2186 private static Type[] IgnorableAttributes = {
2187 // Security related attributes
2188 typeof (System.Reflection.AssemblyKeyFileAttribute),
2189 typeof (System.Reflection.AssemblyDelaySignAttribute),
2190 // Present in @RefType
2191 typeof (System.Runtime.InteropServices.OutAttribute),
2192 // For naming the indexer to use when not using indexers
2193 typeof (System.Reflection.DefaultMemberAttribute),
2194 // for decimal constants
2195 typeof (System.Runtime.CompilerServices.DecimalConstantAttribute),
2196 // compiler generated code
2197 typeof (System.Runtime.CompilerServices.CompilerGeneratedAttribute),
2198 // more compiler generated code, e.g. iterator methods
2199 typeof (System.Diagnostics.DebuggerHiddenAttribute),
2200 typeof (System.Runtime.CompilerServices.FixedBufferAttribute),
2201 typeof (System.Runtime.CompilerServices.UnsafeValueTypeAttribute),
2202 // extension methods
2203 typeof (System.Runtime.CompilerServices.ExtensionAttribute),
2207 private static void MakeAttributes(XmlElement root, object attributes, bool assemblyAttributes) {
2210 object[] at = ((ICustomAttributeProvider) attributes).GetCustomAttributes (false);
2213 System.Collections.Generic.IList<CustomAttributeData> at;
2214 if (attributes is Assembly)
2215 at = CustomAttributeData.GetCustomAttributes((Assembly)attributes);
2216 else if (attributes is MemberInfo)
2217 at = CustomAttributeData.GetCustomAttributes((MemberInfo)attributes);
2218 else if (attributes is Module)
2219 at = CustomAttributeData.GetCustomAttributes((Module)attributes);
2220 else if (attributes is ParameterInfo)
2221 at = CustomAttributeData.GetCustomAttributes((ParameterInfo)attributes);
2223 throw new ArgumentException("unsupported type: " + attributes.GetType().ToString());
2228 ClearElement(root, "Attributes");
2233 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2237 e = root.OwnerDocument.CreateElement("Attributes");
2240 foreach (CustomAttributeData a in at) {
2241 if (!IsPublic (a.Constructor.DeclaringType))
2243 if (slashdocFormatter.GetName (a.Constructor.DeclaringType) == null)
2246 if (Array.IndexOf (IgnorableAttributes, a.Constructor.DeclaringType) >= 0)
2251 StringList fields = new StringList ();
2253 foreach (CustomAttributeTypedArgument f in a.ConstructorArguments) {
2254 fields.Add(MakeAttributesValueString(f.Value));
2256 foreach (CustomAttributeNamedArgument f in a.NamedArguments) {
2257 fields.Add(f.MemberInfo.Name + "=" + MakeAttributesValueString(f.TypedValue.Value));
2260 string a2 = String.Join(", ", DocUtils.ToStringArray (fields));
2261 if (a2 != "") a2 = "(" + a2 + ")";
2263 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2266 string name = a.Constructor.DeclaringType.FullName;
2267 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
2268 WriteElementText(ae, "AttributeName", name + a2);
2271 foreach (Attribute a in at) {
2272 if (!IsPublic (a.GetType ()))
2274 if (slashdocFormatter.GetName (a.GetType ()) == null) continue; // hide non-visible attributes
2275 //if (assemblyAttributes && a.GetType().FullName.StartsWith("System.Reflection.")) continue;
2276 if (a.GetType().FullName == "System.Reflection.AssemblyKeyFileAttribute" || a.GetType().FullName == "System.Reflection.AssemblyDelaySignAttribute") continue; // hide security-related attributes
2280 // There's no way to reconstruct how the attribute's constructor was called,
2281 // so as a substitute, just list the value of all of the attribute's public fields.
2283 StringList fields = new StringList ();
2284 foreach (PropertyInfo f in a.GetType().GetProperties(BindingFlags.Public|BindingFlags.Instance)) {
2285 if (f.Name == "TypeId") continue;
2289 v = f.GetValue(a, null);
2290 if (v == null) v = "null";
2291 else if (v is string) v = "\"" + v + "\"";
2292 else if (v is Type) v = "typeof(" + GetCSharpFullName ((Type)v) + ")";
2293 else if (v is Enum) v = v.GetType().FullName + "." + v.ToString().Replace(", ", "|");
2295 catch (Exception ex) {
2296 v = "/* error getting property value: " + ex.Message + " */";
2299 fields.Add(f.Name + "=" + v);
2301 string a2 = String.Join(", ", DocUtils.ToStringArray (fields));
2302 if (a2 != "") a2 = "(" + a2 + ")";
2304 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2307 string name = a.GetType().FullName;
2308 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
2309 WriteElementText(ae, "AttributeName", name + a2);
2313 if (b && e.ParentNode == null)
2314 root.AppendChild(e);
2316 ClearElement(root, "Attributes");
2318 NormalizeWhitespace(e);
2322 private static string MakeAttributesValueString(object v) {
2323 if (v == null) return "null";
2324 else if (v is string) return "\"" + v + "\"";
2325 else if (v is bool) return (bool)v ? "true" : "false";
2326 else if (v is Type) return "typeof(" + GetCSharpFullName ((Type)v) + ")";
2327 else if (v is Enum) {
2328 string typename = v.GetType ().FullName;
2329 return typename + "." + v.ToString().Replace(", ", " | " + typename + ".");
2331 else return v.ToString();
2335 private static void MakeParameters(XmlElement root, ParameterInfo[] parameters) {
2336 XmlElement e = WriteElement(root, "Parameters");
2338 foreach (ParameterInfo p in parameters) {
2339 XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
2341 pe.SetAttribute("Name", p.Name);
2342 pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
2343 if (p.ParameterType.IsByRef) {
2344 if (p.IsOut) pe.SetAttribute("RefType", "out");
2345 else pe.SetAttribute("RefType", "ref");
2347 MakeAttributes(pe, p, false);
2351 private static void MakeTypeParameters(XmlElement root, Type[] typeParams)
2353 if (typeParams == null || typeParams.Length == 0) {
2354 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2356 root.RemoveChild (f);
2359 XmlElement e = WriteElement(root, "TypeParameters");
2361 foreach (Type t in typeParams) {
2362 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2364 pe.SetAttribute("Name", t.Name);
2365 MakeAttributes(pe, t, false);
2367 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2368 GenericParameterAttributes attrs = t.GenericParameterAttributes;
2369 Type[] constraints = t.GetGenericParameterConstraints ();
2370 if (attrs == GenericParameterAttributes.None && constraints.Length == 0) {
2378 ce = root.OwnerDocument.CreateElement ("Constraints");
2380 pe.AppendChild (ce);
2381 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2382 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2383 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2384 AppendElementText (ce, "ParameterAttribute", "Covariant");
2385 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2386 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2387 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2388 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2389 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2390 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2391 foreach (Type c in constraints) {
2392 AppendElementText (ce,
2393 c.IsInterface ? "InterfaceName" : "BaseTypeName", GetDocTypeFullName (c));
2399 private static void MakeParameters(XmlElement root, MemberInfo mi) {
2400 if (mi is ConstructorInfo) MakeParameters(root, ((ConstructorInfo)mi).GetParameters());
2401 else if (mi is MethodInfo) {
2402 MethodBase mb = (MethodBase) mi;
2403 ParameterInfo[] parameters = mb.GetParameters();
2404 MakeParameters(root, parameters);
2405 if (parameters.Length > 0 && DocUtils.IsExtensionMethod (mb)) {
2406 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2407 p.SetAttribute ("RefType", "this");
2410 else if (mi is PropertyInfo) {
2411 ParameterInfo[] parameters = ((PropertyInfo)mi).GetIndexParameters();
2412 if (parameters.Length > 0)
2413 MakeParameters(root, parameters);
2417 else if (mi is FieldInfo) return;
2418 else if (mi is EventInfo) return;
2419 else throw new ArgumentException();
2422 private static string GetDocParameterType (Type type)
2424 return GetDocTypeFullName (type).Replace ("@", "&");
2427 private static void MakeReturnValue(XmlElement root, Type type, ICustomAttributeProvider attributes) {
2428 XmlElement e = WriteElement(root, "ReturnValue");
2430 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2431 if (attributes != null)
2432 MakeAttributes(e, attributes, false);
2435 private static void MakeReturnValue(XmlElement root, MemberInfo mi) {
2436 if (mi is ConstructorInfo) return;
2437 else if (mi is MethodInfo) MakeReturnValue(root, ((MethodInfo)mi).ReturnType, ((MethodInfo)mi).ReturnTypeCustomAttributes);
2438 else if (mi is PropertyInfo) MakeReturnValue(root, ((PropertyInfo)mi).PropertyType, null);
2439 else if (mi is FieldInfo) MakeReturnValue(root, ((FieldInfo)mi).FieldType, null);
2440 else if (mi is EventInfo) MakeReturnValue(root, ((EventInfo)mi).EventHandlerType, null);
2441 else throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2444 private static XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info) {
2445 MemberInfo mi = info.Member;
2446 if (mi is Type) return null;
2448 string sigs = MakeMemberSignature(mi);
2449 if (sigs == null) return null; // not publicly visible
2451 // no documentation for property/event accessors. Is there a better way of doing this?
2452 if (mi.Name.StartsWith("get_")) return null;
2453 if (mi.Name.StartsWith("set_")) return null;
2454 if (mi.Name.StartsWith("add_")) return null;
2455 if (mi.Name.StartsWith("remove_")) return null;
2456 if (mi.Name.StartsWith("raise_")) return null;
2458 XmlElement me = doc.CreateElement("Member");
2459 me.SetAttribute("MemberName", GetMemberName (mi));
2464 if (since != null) {
2465 XmlNode docs = me.SelectSingleNode("Docs");
2466 docs.AppendChild (CreateSinceNode (doc));
2472 private static string GetMemberName (MemberInfo mi)
2474 MethodBase mb = mi as MethodBase;
2476 PropertyInfo pi = mi as PropertyInfo;
2479 return DocUtils.GetPropertyName (pi);
2481 StringBuilder sb = new StringBuilder (mi.Name.Length);
2482 if (!DocUtils.IsExplicitlyImplemented (mb))
2483 sb.Append (mi.Name);
2486 MethodInfo ifaceMethod;
2487 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2488 sb.Append (GetDocTypeFullName (iface));
2490 sb.Append (ifaceMethod.Name);
2492 if (DocUtils.GetContainsGenericParameters (mb)) {
2493 Type[] typeParams = DocUtils.GetGenericArguments (mb);
2494 if (typeParams.Length > 0) {
2496 sb.Append (typeParams [0].Name);
2497 for (int i = 1; i < typeParams.Length; ++i)
2498 sb.Append (",").Append (typeParams [i].Name);
2502 return sb.ToString ();
2505 private static int CountChars (string s, char c)
2508 for (int i = 0; i < s.Length; ++i) {
2515 static bool IsDelegate(Type type) {
2516 return typeof(System.Delegate).IsAssignableFrom (type) && !type.IsAbstract;
2519 /// SIGNATURE GENERATION FUNCTIONS
2521 private static bool InterfaceNotFromAnother(Type i, Type[] i2) {
2522 foreach (Type t in i2)
2523 if (i != t && Array.IndexOf(t.GetInterfaces(), i) != -1)
2528 static string MakeTypeSignature (Type type) {
2529 return csharpFormatter.GetDeclaration (type);
2532 static string MakeMemberSignature(MemberInfo mi) {
2533 return csharpFullFormatter.GetDeclaration (mi);
2536 static string GetMemberType(MemberInfo mi) {
2537 if (mi is ConstructorInfo) return "Constructor";
2538 if (mi is MethodInfo) return "Method";
2539 if (mi is PropertyInfo) return "Property";
2540 if (mi is FieldInfo) return "Field";
2541 if (mi is EventInfo) return "Event";
2542 throw new ArgumentException();
2545 private static string GetDocTypeName (Type type)
2547 return docTypeFormatter.GetName (type);
2550 private static string GetDocTypeFullName (Type type)
2552 return DocTypeFullMemberFormatter.Default.GetName (type);
2555 private static string GetCSharpFullName (Type type)
2557 return DocTypeFullMemberFormatter.Default.GetName (type);
2560 class DocsNodeInfo {
2561 public DocsNodeInfo (XmlElement node)
2566 public DocsNodeInfo (XmlElement node, Type type)
2572 public DocsNodeInfo (XmlElement node, MemberInfo member)
2575 SetMemberInfo (member);
2578 public void SetType (Type type)
2581 throw new ArgumentNullException ("type");
2582 GenericParameters = DocUtils.GetGenericArguments (type);
2583 if (type.DeclaringType != null) {
2584 Type[] declGenParams = DocUtils.GetGenericArguments (type.DeclaringType);
2585 if (declGenParams != null && GenericParameters.Length == declGenParams.Length) {
2586 GenericParameters = null;
2588 else if (declGenParams != null) {
2589 Type[] nestedParams = new Type [GenericParameters.Length - declGenParams.Length];
2590 for (int i = 0; i < nestedParams.Length; ++i) {
2591 nestedParams [i] = GenericParameters [i+declGenParams.Length];
2593 GenericParameters = nestedParams;
2596 if (IsDelegate(type)) {
2597 Parameters = type.GetMethod("Invoke").GetParameters();
2598 ReturnType = type.GetMethod("Invoke").ReturnType;
2600 SetSlashDocs (type);
2603 public void SetMemberInfo (MemberInfo member)
2606 throw new ArgumentNullException ("member");
2607 ReturnIsReturn = true;
2611 if (member is MethodInfo || member is ConstructorInfo) {
2612 Parameters = ((MethodBase) member).GetParameters ();
2613 if (DocUtils.GetContainsGenericParameters ((MethodBase) member)) {
2614 GenericParameters = DocUtils.GetGenericArguments ((MethodBase) member);
2617 else if (member is PropertyInfo) {
2618 Parameters = ((PropertyInfo) member).GetIndexParameters ();
2621 if (member is MethodInfo) {
2622 ReturnType = ((MethodInfo) member).ReturnType;
2623 } else if (member is PropertyInfo) {
2624 ReturnType = ((PropertyInfo) member).PropertyType;
2625 ReturnIsReturn = false;
2628 // no remarks section for enum members
2629 if (member.DeclaringType != null && member.DeclaringType.IsEnum)
2631 SetSlashDocs (member);
2634 private void SetSlashDocs (MemberInfo member)
2636 if (slashdocs == null)
2639 string slashdocsig = slashdocFormatter.GetDeclaration (member);
2640 if (slashdocsig != null)
2641 SlashDocs = slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
2644 public Type ReturnType;
2645 public Type[] GenericParameters;
2646 public ParameterInfo[] Parameters;
2647 public bool ReturnIsReturn;
2648 public XmlElement Node;
2649 public bool AddRemarks = true;
2650 public XmlNode SlashDocs;
2651 public XmlReader EcmaDocs;
2652 public MemberInfo Member;
2655 static string GetXPathForMember (DocumentationMember member)
2657 StringBuilder xpath = new StringBuilder ();
2658 xpath.Append ("//Members/Member[@MemberName=\"")
2659 .Append (member.MemberName)
2661 if (member.Parameters != null && member.Parameters.Count > 0) {
2662 xpath.Append ("/Parameters[count(Parameter) = ")
2663 .Append (member.Parameters.Count);
2664 for (int i = 0; i < member.Parameters.Count; ++i) {
2665 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2666 xpath.Append (member.Parameters [i]);
2667 xpath.Append ("\"");
2669 xpath.Append ("]/..");
2671 return xpath.ToString ();
2674 public static string GetXPathForMember (XPathNavigator member)
2676 StringBuilder xpath = new StringBuilder ();
2677 xpath.Append ("//Type[@FullName=\"")
2678 .Append (SelectSingleNode (member, "../../@FullName").Value)
2680 xpath.Append ("Members/Member[@MemberName=\"")
2681 .Append (SelectSingleNode (member, "@MemberName").Value)
2683 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2684 if (parameters.Count > 0) {
2685 xpath.Append ("/Parameters[count(Parameter) = ")
2686 .Append (parameters.Count);
2688 while (parameters.MoveNext ()) {
2690 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2691 xpath.Append (parameters.Current.Value);
2692 xpath.Append ("\"");
2694 xpath.Append ("]/..");
2696 return xpath.ToString ();
2699 public static string GetXPathForMember (MemberInfo member)
2701 StringBuilder xpath = new StringBuilder ();
2702 xpath.Append ("//Type[@FullName=\"")
2703 .Append (member.DeclaringType.FullName)
2705 xpath.Append ("Members/Member[@MemberName=\"")
2706 .Append (GetMemberName (member))
2709 ParameterInfo[] parameters = null;
2710 if (member is MethodBase)
2711 parameters = ((MethodBase) member).GetParameters ();
2712 else if (member is PropertyInfo) {
2713 parameters = ((PropertyInfo) member).GetIndexParameters ();
2715 if (parameters != null && parameters.Length > 0) {
2716 xpath.Append ("/Parameters[count(Parameter) = ")
2717 .Append (parameters.Length);
2718 for (int i = 0; i < parameters.Length; ++i) {
2719 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2720 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2721 xpath.Append ("\"");
2723 xpath.Append ("]/..");
2725 return xpath.ToString ();
2729 static class DocUtils {
2730 public static bool GetContainsGenericParameters (Type type)
2735 return type.ContainsGenericParameters;
2739 public static bool GetContainsGenericParameters (MethodBase mb)
2744 return mb.ContainsGenericParameters;
2748 public static Type[] GetGenericArguments (Type type)
2751 return new Type [0];
2753 return type.GetGenericArguments ();
2757 public static Type[] GetGenericArguments (MethodBase mb)
2760 return new Type [0];
2762 return mb.GetGenericArguments ();
2766 public static Type GetGenericTypeDefinition (Type type)
2771 return type.GetGenericTypeDefinition ();
2775 public static Type[] GetGenericParameterConstraints (Type type)
2780 return type.GetGenericParameterConstraints ();
2784 public static bool IsGenericType (Type type)
2789 return type.IsGenericType;
2793 public static bool IsGenericParameter (Type type)
2798 return type.IsGenericParameter;
2802 public static bool IsExplicitlyImplemented (MethodBase method)
2804 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2807 public static string GetTypeDotMember (string name)
2809 int startType, startMethod;
2810 startType = startMethod = -1;
2811 for (int i = 0; i < name.Length; ++i) {
2812 if (name [i] == '.') {
2813 startType = startMethod;
2817 return name.Substring (startType+1);
2820 public static string GetMember (string name)
2822 int i = name.LastIndexOf ('.');
2825 return name.Substring (i+1);
2828 public static void GetInfoForExplicitlyImplementedMethod (
2829 MethodBase method, out Type iface, out MethodInfo ifaceMethod)
2831 Type declType = method.DeclaringType;
2832 foreach (Type declIface in declType.GetInterfaces ()) {
2833 InterfaceMapping map = declType.GetInterfaceMap (declIface);
2834 for (int i = 0; i < map.TargetMethods.Length; ++i)
2835 if (method == map.TargetMethods [i]) {
2836 iface = map.InterfaceType;
2837 ifaceMethod = map.InterfaceMethods [i];
2841 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2844 public static string[] ToStringArray (StringList list)
2847 return (string[]) list.ToArray (typeof(string));
2849 return list.ToArray ();
2853 public static string GetPropertyName (PropertyInfo pi)
2855 // Issue: (g)mcs-generated assemblies that explicitly implement
2856 // properties don't specify the full namespace, just the
2857 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2858 MethodInfo method = pi.GetGetMethod (true);
2860 method = pi.GetSetMethod (true);
2861 if (!IsExplicitlyImplemented (method))
2864 // Need to determine appropriate namespace for this member.
2866 MethodInfo ifaceMethod;
2867 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2868 return string.Join (".", new string[]{
2869 DocTypeFullMemberFormatter.Default.GetName (iface),
2870 GetMember (pi.Name)});
2873 public static string PathCombine (string dir, string path)
2879 return Path.Combine (dir, path);
2882 public static bool IsExtensionMethod (MethodBase method)
2888 method.GetCustomAttributes (
2889 typeof(System.Runtime.CompilerServices.ExtensionAttribute),
2890 false).Length != 0 &&
2891 method.DeclaringType.GetCustomAttributes (
2892 typeof(System.Runtime.CompilerServices.ExtensionAttribute),
2898 class DocumentationMember {
2899 public StringToStringMap MemberSignatures = new StringToStringMap ();
2900 public string ReturnType;
2901 public StringList Parameters;
2902 public string MemberName;
2903 public string MemberType;
2905 public DocumentationMember (XmlReader reader)
2907 MemberName = reader.GetAttribute ("MemberName");
2908 int depth = reader.Depth;
2910 StringList p = new StringList ();
2912 if (reader.NodeType != XmlNodeType.Element)
2914 switch (reader.Name) {
2915 case "MemberSignature":
2916 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
2919 MemberType = reader.ReadElementString ();
2922 if (reader.Depth == depth + 2)
2923 ReturnType = reader.ReadElementString ();
2926 if (reader.Depth == depth + 2)
2927 p.Add (reader.GetAttribute ("Type"));
2930 if (reader.Depth == depth + 1)
2934 } while (go && reader.Read () && reader.Depth >= depth);
2940 public DocumentationMember (XmlNode node)
2942 MemberName = node.Attributes ["MemberName"].Value;
2943 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
2944 XmlAttribute l = n.Attributes ["Language"];
2945 XmlAttribute v = n.Attributes ["Value"];
2946 if (l != null && v != null)
2947 MemberSignatures [l.Value] = v.Value;
2949 MemberType = node.SelectSingleNode ("MemberType").InnerText;
2950 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
2952 ReturnType = rt.InnerText;
2953 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
2955 Parameters = new StringList (p.Count);
2956 for (int i = 0; i < p.Count; ++i)
2957 Parameters.Add (p [i].Attributes ["Type"].Value);
2962 public abstract class MemberFormatter {
2963 public string GetName (MemberInfo member)
2965 Type type = member as Type;
2967 return GetTypeName (type);
2968 ConstructorInfo ctor = member as ConstructorInfo;
2970 return GetConstructorName (ctor);
2971 MethodInfo method = member as MethodInfo;
2973 return GetMethodName (method);
2974 PropertyInfo prop = member as PropertyInfo;
2976 return GetPropertyName (prop);
2977 FieldInfo field = member as FieldInfo;
2979 return GetFieldName (field);
2980 EventInfo e = member as EventInfo;
2982 return GetEventName (e);
2983 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
2986 protected virtual string GetTypeName (Type type)
2989 throw new ArgumentNullException ("type");
2990 return _AppendTypeName (new StringBuilder (type.Name.Length), type).ToString ();
2993 protected virtual char[] ArrayDelimeters {
2994 get {return new char[]{'[', ']'};}
2997 protected StringBuilder _AppendTypeName (StringBuilder buf, Type type)
3000 _AppendTypeName (buf, type.GetElementType ()).Append (ArrayDelimeters [0]);
3001 int rank = type.GetArrayRank ();
3003 buf.Append (new string (',', rank-1));
3004 return buf.Append (ArrayDelimeters [1]);
3007 return AppendRefTypeName (buf, type);
3009 if (type.IsPointer) {
3010 return AppendPointerTypeName (buf, type);
3012 AppendNamespace (buf, type);
3013 if (DocUtils.IsGenericParameter (type)) {
3014 return AppendTypeName (buf, type);
3016 if (!DocUtils.IsGenericType (type)) {
3017 if (type.DeclaringType != null)
3018 AppendTypeName (buf, type.DeclaringType).Append (NestedTypeSeparator);
3019 return AppendTypeName (buf, type);
3021 return AppendGenericType (buf, type);
3024 protected virtual StringBuilder AppendNamespace (StringBuilder buf, Type type)
3026 if (type.Namespace != null && type.Namespace.Length > 0)
3027 buf.Append (type.Namespace).Append ('.');
3031 protected virtual StringBuilder AppendTypeName (StringBuilder buf, Type type)
3033 return AppendTypeName (buf, type.Name);
3036 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3038 int n = typename.IndexOf ("`");
3040 return buf.Append (typename.Substring (0, n));
3041 return buf.Append (typename);
3044 protected virtual string RefTypeModifier {
3048 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, Type type)
3050 return _AppendTypeName (buf, type.GetElementType ()).Append (RefTypeModifier);
3053 protected virtual string PointerModifier {
3057 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, Type type)
3059 return _AppendTypeName (buf, type.GetElementType ()).Append (PointerModifier);
3062 protected virtual char[] GenericTypeContainer {
3063 get {return new char[]{'<', '>'};}
3066 protected virtual char NestedTypeSeparator {
3070 protected virtual StringBuilder AppendGenericType (StringBuilder buf, Type type)
3072 Type[] genArgs = DocUtils.GetGenericArguments (type);
3074 if (type.DeclaringType != null) {
3075 AppendTypeName (buf, type.DeclaringType);
3076 if (DocUtils.IsGenericType (type.DeclaringType)) {
3077 buf.Append (GenericTypeContainer [0]);
3078 int max = DocUtils.GetGenericArguments (type.DeclaringType).Length;
3079 _AppendTypeName (buf, genArgs [genArg++]);
3080 while (genArg < max) {
3082 _AppendTypeName (buf, genArgs [genArg++]);
3084 buf.Append (GenericTypeContainer [1]);
3086 buf.Append (NestedTypeSeparator);
3088 AppendTypeName (buf, type);
3089 if (genArg < genArgs.Length) {
3090 buf.Append (GenericTypeContainer [0]);
3091 _AppendTypeName (buf, genArgs [genArg++]);
3092 while (genArg < genArgs.Length) {
3094 _AppendTypeName (buf, genArgs [genArg++]);
3096 buf.Append (GenericTypeContainer [1]);
3101 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, Type type)
3106 protected virtual string GetConstructorName (ConstructorInfo constructor)
3108 return constructor.Name;
3111 protected virtual string GetMethodName (MethodInfo method)
3116 protected virtual string GetPropertyName (PropertyInfo property)
3118 return property.Name;
3121 protected virtual string GetFieldName (FieldInfo field)
3126 protected virtual string GetEventName (EventInfo e)
3131 public string GetDeclaration (MemberInfo member)
3133 Type type = member as Type;
3135 return GetTypeDeclaration (type);
3136 ConstructorInfo ctor = member as ConstructorInfo;
3138 return GetConstructorDeclaration (ctor);
3139 MethodInfo method = member as MethodInfo;
3141 return GetMethodDeclaration (method);
3142 PropertyInfo prop = member as PropertyInfo;
3144 return GetPropertyDeclaration (prop);
3145 FieldInfo field = member as FieldInfo;
3147 return GetFieldDeclaration (field);
3148 EventInfo e = member as EventInfo;
3150 return GetEventDeclaration (e);
3151 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3154 protected virtual string GetTypeDeclaration (Type type)
3157 throw new ArgumentNullException ("type");
3158 StringBuilder buf = new StringBuilder (type.Name.Length);
3159 _AppendTypeName (buf, type);
3160 AppendGenericTypeConstraints (buf, type);
3161 return buf.ToString ();
3164 protected virtual string GetConstructorDeclaration (ConstructorInfo constructor)
3166 return GetConstructorName (constructor);
3169 protected virtual string GetMethodDeclaration (MethodInfo method)
3171 // Special signature for destructors.
3172 if (method.Name == "Finalize" && method.GetParameters().Length == 0)
3173 return GetFinalizerName (method);
3175 StringBuilder buf = new StringBuilder ();
3177 AppendVisibility (buf, method);
3178 if (buf.Length == 0 &&
3179 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3182 AppendModifiers (buf, method);
3184 if (buf.Length != 0)
3186 buf.Append (GetName (method.ReturnType)).Append (" ");
3188 AppendMethodName (buf, method);
3189 AppendGenericMethod (buf, method).Append (" ");
3190 AppendParameters (buf, method, method.GetParameters ());
3191 AppendGenericMethodConstraints (buf, method);
3192 return buf.ToString ();
3195 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodBase method)
3197 return buf.Append (method.Name);
3200 protected virtual string GetFinalizerName (MethodInfo method)
3205 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodBase method)
3210 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodInfo method)
3215 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodInfo method)
3220 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodBase method, ParameterInfo[] parameters)
3225 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodInfo method)
3230 protected virtual string GetPropertyDeclaration (PropertyInfo property)
3232 return GetPropertyName (property);
3235 protected virtual string GetFieldDeclaration (FieldInfo field)
3237 return GetFieldName (field);
3240 protected virtual string GetEventDeclaration (EventInfo e)
3242 return GetEventName (e);
3246 class CSharpFullMemberFormatter : MemberFormatter {
3248 protected override StringBuilder AppendNamespace (StringBuilder buf, Type type)
3250 if (GetCSharpType (type.FullName) == null && type.Namespace != null && type.Namespace.Length > 0 && type.Namespace != "System")
3251 buf.Append (type.Namespace).Append ('.');
3255 private string GetCSharpType (string t)
3258 case "System.Byte": return "byte";
3259 case "System.SByte": return "sbyte";
3260 case "System.Int16": return "short";
3261 case "System.Int32": return "int";
3262 case "System.Int64": return "long";
3264 case "System.UInt16": return "ushort";
3265 case "System.UInt32": return "uint";
3266 case "System.UInt64": return "ulong";
3268 case "System.Single": return "float";
3269 case "System.Double": return "double";
3270 case "System.Decimal": return "decimal";
3271 case "System.Boolean": return "bool";
3272 case "System.Char": return "char";
3273 case "System.Void": return "void";
3274 case "System.String": return "string";
3275 case "System.Object": return "object";
3280 protected override StringBuilder AppendTypeName (StringBuilder buf, Type type)
3282 if (DocUtils.IsGenericParameter (type))
3283 return buf.Append (type.Name);
3284 string t = type.FullName;
3285 if (!t.StartsWith ("System.")) {
3286 return base.AppendTypeName (buf, type);
3289 string s = GetCSharpType (t);
3291 return buf.Append (s);
3293 return base.AppendTypeName (buf, type);
3296 protected override string GetTypeDeclaration (Type type)
3298 string visibility = GetTypeVisibility (type.Attributes);
3299 if (visibility == null)
3302 StringBuilder buf = new StringBuilder ();
3304 buf.Append (visibility);
3307 MemberFormatter full = new CSharpFullMemberFormatter ();
3309 if (IsDelegate(type)) {
3310 buf.Append("delegate ");
3311 MethodInfo invoke = type.GetMethod ("Invoke");
3312 buf.Append (full.GetName (invoke.ReturnType)).Append (" ");
3313 buf.Append (GetName (type));
3314 AppendParameters (buf, invoke, invoke.GetParameters ());
3315 AppendGenericTypeConstraints (buf, type);
3318 return buf.ToString();
3321 if (type.IsAbstract && !type.IsInterface)
3322 buf.Append("abstract ");
3323 if (type.IsSealed && !IsDelegate(type) && !type.IsValueType)
3324 buf.Append("sealed ");
3325 buf.Replace ("abstract sealed", "static");
3327 buf.Append (GetTypeKind (type));
3329 buf.Append (GetCSharpType (type.FullName) == null
3334 Type basetype = type.BaseType;
3335 if (basetype == typeof(object) || type.IsValueType) // don't show this in signatures
3338 ArrayList interface_names = new ArrayList ();
3339 foreach (Type i in type.GetInterfaces ())
3340 if ((type.BaseType == null || Array.IndexOf (type.BaseType.GetInterfaces (), i) == -1) &&
3341 InterfaceNotFromAnother (i, type.GetInterfaces ()))
3342 interface_names.Add (full.GetName (i));
3343 interface_names.Sort ();
3345 if (basetype != null || interface_names.Count > 0)
3348 if (basetype != null) {
3349 buf.Append (full.GetName (basetype));
3350 if (interface_names.Count > 0)
3354 for (int i = 0; i < interface_names.Count; i++){
3357 buf.Append (interface_names [i]);
3359 AppendGenericTypeConstraints (buf, type);
3362 return buf.ToString ();
3365 static string GetTypeKind (Type t)
3369 if (t.IsClass || t == typeof(System.Enum))
3375 throw new ArgumentException(t.FullName);
3378 static string GetTypeVisibility (TypeAttributes ta)
3380 switch (ta & TypeAttributes.VisibilityMask) {
3381 case TypeAttributes.Public:
3382 case TypeAttributes.NestedPublic:
3385 case TypeAttributes.NestedFamily:
3386 case TypeAttributes.NestedFamORAssem:
3394 static bool IsDelegate(Type type)
3396 return typeof (System.Delegate).IsAssignableFrom (type) && !type.IsAbstract;
3399 private static bool InterfaceNotFromAnother(Type i, Type[] i2)
3401 foreach (Type t in i2)
3402 if (i != t && Array.IndexOf (t.GetInterfaces(), i) != -1)
3407 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, Type type)
3409 if (!DocUtils.GetContainsGenericParameters (type))
3411 return AppendConstraints (buf, DocUtils.GetGenericArguments (type));
3414 private StringBuilder AppendConstraints (StringBuilder buf, Type[] genArgs)
3417 foreach (Type genArg in genArgs) {
3418 GenericParameterAttributes attrs = genArg.GenericParameterAttributes;
3419 Type[] constraints = genArg.GetGenericParameterConstraints ();
3420 if (attrs == GenericParameterAttributes.None && constraints.Length == 0)
3422 buf.Append (" where ").Append (genArg.Name).Append (" : ");
3423 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
3424 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
3425 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
3428 buf.Append ("class");
3432 buf.Append ("struct");
3435 if (constraints.Length > 0 && !isvt) {
3438 buf.Append (GetTypeName (constraints [0]));
3439 for (int i = 1; i < constraints.Length; ++i)
3440 buf.Append (", ").Append (GetTypeName (constraints [i]));
3442 if (isnew && !isvt) {
3445 buf.Append ("new()");
3452 protected override string GetConstructorDeclaration (ConstructorInfo constructor)
3454 StringBuilder buf = new StringBuilder ();
3455 AppendVisibility (buf, constructor);
3456 if (buf.Length == 0)
3460 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
3461 AppendParameters (buf, constructor, constructor.GetParameters ());
3464 return buf.ToString ();
3467 protected override string GetMethodDeclaration (MethodInfo method)
3469 string decl = base.GetMethodDeclaration (method);
3475 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodBase method)
3477 if (DocUtils.IsExplicitlyImplemented (method)) {
3479 MethodInfo ifaceMethod;
3480 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3481 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3483 .Append (ifaceMethod.Name);
3485 return base.AppendMethodName (buf, method);
3488 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodInfo method)
3490 if (!DocUtils.GetContainsGenericParameters (method))
3492 return AppendConstraints (buf, DocUtils.GetGenericArguments (method));
3495 protected override string RefTypeModifier {
3499 protected override string GetFinalizerName (MethodInfo method)
3501 return "~" + method.DeclaringType.Name + " ()";
3504 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodBase method)
3508 if (method.IsPublic)
3509 return buf.Append ("public");
3510 if (method.IsFamily || method.IsFamilyOrAssembly)
3511 return buf.Append ("protected");
3515 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodInfo method)
3517 string modifiers = String.Empty;
3518 if (method.IsStatic) modifiers += " static";
3519 if (method.IsVirtual && !method.IsAbstract) {
3520 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3521 else modifiers += " override";
3523 if (method.IsAbstract && !method.DeclaringType.IsInterface) modifiers += " abstract";
3524 if (method.IsFinal) modifiers += " sealed";
3525 if (modifiers == " virtual sealed") modifiers = "";
3527 return buf.Append (modifiers);
3530 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodInfo method)
3532 if (DocUtils.GetContainsGenericParameters (method)) {
3533 Type[] args = DocUtils.GetGenericArguments (method);
3534 if (args.Length > 0) {
3536 buf.Append (args [0].Name);
3537 for (int i = 1; i < args.Length; ++i)
3538 buf.Append (",").Append (args [i].Name);
3545 protected override StringBuilder AppendParameters (StringBuilder buf, MethodBase method, ParameterInfo[] parameters)
3547 return AppendParameters (buf, method, parameters, '(', ')');
3550 private StringBuilder AppendParameters (StringBuilder buf, MethodBase method, ParameterInfo[] parameters, char begin, char end)
3554 if (parameters.Length > 0) {
3555 if (DocUtils.IsExtensionMethod (method))
3556 buf.Append ("this ");
3557 AppendParameter (buf, parameters [0]);
3558 for (int i = 1; i < parameters.Length; ++i) {
3560 AppendParameter (buf, parameters [i]);
3564 return buf.Append (end);
3567 private StringBuilder AppendParameter (StringBuilder buf, ParameterInfo parameter)
3569 if (parameter.ParameterType.IsByRef) {
3570 if (parameter.IsOut)
3571 buf.Append ("out ");
3573 buf.Append ("ref ");
3575 buf.Append (GetName (parameter.ParameterType)).Append (" ");
3576 return buf.Append (parameter.Name);
3579 protected override string GetPropertyDeclaration (PropertyInfo property)
3583 string get_visible = null;
3584 if ((method = property.GetGetMethod (true)) != null &&
3585 (DocUtils.IsExplicitlyImplemented (method) ||
3586 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3587 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3588 string set_visible = null;
3589 if ((method = property.GetSetMethod (true)) != null &&
3590 (DocUtils.IsExplicitlyImplemented (method) ||
3591 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3592 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3594 if ((set_visible == null) && (get_visible == null))
3598 StringBuilder buf = new StringBuilder ();
3599 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
3600 buf.Append (visibility = get_visible);
3601 else if (set_visible != null && get_visible == null)
3602 buf.Append (visibility = set_visible);
3604 buf.Append (visibility = "public");
3606 // Pick an accessor to use for static/virtual/override/etc. checks.
3607 method = property.GetSetMethod (true);
3609 method = property.GetGetMethod (true);
3611 string modifiers = String.Empty;
3612 if (method.IsStatic) modifiers += " static";
3613 if (method.IsVirtual && !method.IsAbstract) {
3614 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
3615 modifiers += " virtual";
3617 modifiers += " override";
3619 if (method.IsAbstract && !method.DeclaringType.IsInterface)
3620 modifiers += " abstract";
3622 modifiers += " sealed";
3623 if (modifiers == " virtual sealed")
3625 buf.Append (modifiers).Append (' ');
3627 buf.Append (GetName (property.PropertyType)).Append (' ');
3629 MemberInfo[] defs = property.DeclaringType.GetDefaultMembers ();
3630 string name = property.Name;
3631 foreach (MemberInfo mi in defs) {
3632 if (mi == property) {
3637 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
3639 if (property.GetIndexParameters ().Length != 0) {
3640 AppendParameters (buf, method, property.GetIndexParameters (), '[', ']');
3644 if (set_visible != null) {
3645 if (set_visible != visibility)
3646 buf.Append (' ').Append (set_visible);
3647 buf.Append (" set;");
3649 if (get_visible != null) {
3650 if (get_visible != visibility)
3651 buf.Append (' ').Append (get_visible);
3652 buf.Append (" get;");
3656 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
3659 protected override string GetFieldDeclaration (FieldInfo field)
3661 if (field.DeclaringType.IsEnum && field.Name == "value__")
3662 return null; // This member of enums aren't documented.
3664 StringBuilder buf = new StringBuilder ();
3665 AppendFieldVisibility (buf, field);
3666 if (buf.Length == 0)
3669 if (field.DeclaringType.IsEnum)
3672 if (field.IsStatic && !field.IsLiteral)
3673 buf.Append (" static");
3674 if (field.IsInitOnly)
3675 buf.Append (" readonly");
3676 if (field.IsLiteral)
3677 buf.Append (" const");
3679 buf.Append (' ').Append (GetName (field.FieldType)).Append (' ');
3680 buf.Append (field.Name);
3681 AppendFieldValue (buf, field);
3684 return buf.ToString ();
3687 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldInfo field)
3690 return buf.Append ("public");
3691 if (field.IsFamily || field.IsFamilyOrAssembly)
3692 return buf.Append ("protected");
3696 static StringBuilder AppendFieldValue (StringBuilder buf, FieldInfo field)
3698 // enums have a value__ field, which we ignore, and FieldInfo.GetValue()
3699 // on a GenericType results in InvalidOperationException
3700 if (field.DeclaringType.IsEnum ||
3701 DocUtils.IsGenericType (field.DeclaringType))
3703 if (field.IsLiteral || (field.IsStatic && field.IsInitOnly)) {
3706 val = field.GetValue (null);
3711 buf.Append (" = ").Append ("null");
3712 else if (val is Enum)
3713 buf.Append (" = ").Append (val.ToString ());
3714 else if (val is IFormattable) {
3715 string value = ((IFormattable)val).ToString();
3717 value = "\"" + value + "\"";
3718 buf.Append (" = ").Append (value);
3724 protected override string GetEventDeclaration (EventInfo e)
3726 StringBuilder buf = new StringBuilder ();
3727 if (AppendVisibility (buf, e.GetAddMethod (true)).Length == 0) {
3731 AppendModifiers (buf, e.GetAddMethod (true));
3733 buf.Append (" event ");
3734 buf.Append (GetName (e.EventHandlerType)).Append (' ');
3735 buf.Append (e.Name).Append (';');
3737 return buf.ToString ();
3741 class CSharpMemberFormatter : CSharpFullMemberFormatter {
3742 protected override StringBuilder AppendNamespace (StringBuilder buf, Type type)
3748 class DocTypeFullMemberFormatter : MemberFormatter {
3749 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
3751 protected override char NestedTypeSeparator {
3756 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
3757 protected override StringBuilder AppendNamespace (StringBuilder buf, Type type)
3763 class SlashDocMemberFormatter : MemberFormatter {
3765 protected override char[] GenericTypeContainer {
3766 get {return new char[]{'{', '}'};}
3769 private bool AddTypeCount = true;
3771 protected override string GetTypeName (Type type)
3773 return base.GetTypeName (type);
3776 private Type genDeclType;
3777 private MethodBase genDeclMethod;
3779 protected override StringBuilder AppendTypeName (StringBuilder buf, Type type)
3781 if (DocUtils.IsGenericParameter (type)) {
3783 if (genDeclType != null) {
3784 Type[] genArgs = DocUtils.GetGenericArguments (genDeclType);
3785 for (int i = 0; i < genArgs.Length; ++i) {
3786 if (genArgs [i].Name == type.Name) {
3787 buf.Append ('`').Append (i);
3792 if (genDeclMethod != null) {
3793 Type[] genArgs = null;
3794 if (DocUtils.GetContainsGenericParameters (genDeclMethod)) {
3795 genArgs = DocUtils.GetGenericArguments (genDeclMethod);
3798 genArgs = new Type[0];
3799 for (int i = 0; i < genArgs.Length; ++i) {
3800 if (genArgs [i].Name == type.Name) {
3801 buf.Append ("``").Append (i);
3806 if (genDeclType == null && genDeclMethod == null) {
3807 // Probably from within an explicitly implemented interface member,
3808 // where CSC uses parameter names instead of indices (why?), e.g.
3809 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
3810 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
3811 buf.Append (type.Name);
3813 if (buf.Length == l) {
3814 throw new Exception (string.Format (
3815 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
3816 type.Name, genDeclType, genDeclMethod));
3820 base.AppendTypeName (buf, type);
3822 int numArgs = DocUtils.GetGenericArguments (type).Length;
3823 if (type.DeclaringType != null)
3824 numArgs -= DocUtils.GetGenericArguments (type).Length;
3826 buf.Append ('`').Append (numArgs);
3833 protected override StringBuilder AppendGenericType (StringBuilder buf, Type type)
3836 base.AppendGenericType (buf, type);
3838 AppendType (buf, type);
3842 private StringBuilder AppendType (StringBuilder buf, Type type)
3844 int numArgs = DocUtils.GetGenericArguments (type).Length;
3845 if (type.DeclaringType != null) {
3846 AppendType (buf, type.DeclaringType).Append (NestedTypeSeparator);
3847 numArgs -= DocUtils.GetGenericArguments (type.DeclaringType).Length;
3849 base.AppendTypeName (buf, type);
3851 buf.Append ('`').Append (numArgs);
3856 protected override string GetConstructorName (ConstructorInfo constructor)
3858 return GetMethodBaseName (constructor, "#ctor");
3861 protected override string GetMethodName (MethodInfo method)
3864 if (!DocUtils.IsExplicitlyImplemented (method))
3868 MethodInfo ifaceMethod;
3869 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3870 AddTypeCount = false;
3871 name = GetTypeName (iface) + "." + ifaceMethod.Name;
3872 AddTypeCount = true;
3874 return GetMethodBaseName (method, name);
3877 private string GetMethodBaseName (MethodBase method, string name)
3879 StringBuilder buf = new StringBuilder ();
3880 buf.Append (GetTypeName (method.DeclaringType));
3882 buf.Append (name.Replace (".", "#"));
3883 if (DocUtils.GetContainsGenericParameters (method)) {
3884 Type[] genArgs = DocUtils.GetGenericArguments (method);
3885 if (genArgs.Length > 0)
3886 buf.Append ("``").Append (genArgs.Length);
3888 ParameterInfo[] parameters = method.GetParameters ();
3889 genDeclType = method.DeclaringType;
3890 genDeclMethod = method;
3891 AppendParameters (buf, DocUtils.GetGenericArguments (method.DeclaringType), parameters);
3893 genDeclMethod = null;
3894 return buf.ToString ();
3897 private StringBuilder AppendParameters (StringBuilder buf, Type[] genArgs, ParameterInfo[] parameters)
3899 if (parameters.Length == 0)
3904 AppendParameter (buf, genArgs, parameters [0]);
3905 for (int i = 1; i < parameters.Length; ++i) {
3907 AppendParameter (buf, genArgs, parameters [i]);
3910 return buf.Append (')');
3913 private StringBuilder AppendParameter (StringBuilder buf, Type[] genArgs, ParameterInfo parameter)
3915 AddTypeCount = false;
3916 buf.Append (GetTypeName (parameter.ParameterType));
3917 AddTypeCount = true;
3921 protected override string GetPropertyName (PropertyInfo property)
3925 MethodInfo method = property.GetGetMethod (true);
3927 method = property.GetSetMethod (true);
3928 if (!DocUtils.IsExplicitlyImplemented (method))
3929 name = property.Name;
3932 MethodInfo ifaceMethod;
3933 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3934 AddTypeCount = false;
3935 name = string.Join ("#", new string[]{
3936 GetTypeName (iface).Replace (".", "#"),
3937 DocUtils.GetMember (property.Name)
3939 AddTypeCount = true;
3942 StringBuilder buf = new StringBuilder ();
3943 buf.Append (GetName (property.DeclaringType));
3946 ParameterInfo[] parameters = property.GetIndexParameters ();
3947 if (parameters.Length > 0) {
3948 genDeclType = property.DeclaringType;
3950 Type[] genArgs = DocUtils.GetGenericArguments (property.DeclaringType);
3951 AppendParameter (buf, genArgs, parameters [0]);
3952 for (int i = 1; i < parameters.Length; ++i) {
3954 AppendParameter (buf, genArgs, parameters [i]);
3959 return buf.ToString ();
3962 protected override string GetFieldName (FieldInfo field)
3964 return string.Format ("{0}.{1}",
3965 GetName (field.DeclaringType), field.Name);
3968 protected override string GetEventName (EventInfo e)
3970 return string.Format ("{0}.{1}",
3971 GetName (e.DeclaringType), e.Name);
3974 protected override string GetTypeDeclaration (Type type)
3976 string name = GetName (type);
3982 protected override string GetConstructorDeclaration (ConstructorInfo constructor)
3984 string name = GetName (constructor);
3990 protected override string GetMethodDeclaration (MethodInfo method)
3992 string name = GetName (method);
3995 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
3996 genDeclType = method.DeclaringType;
3997 genDeclMethod = method;
3998 name += "~" + GetName (method.ReturnType);
4000 genDeclMethod = null;
4005 protected override string GetPropertyDeclaration (PropertyInfo property)
4007 string name = GetName (property);
4013 protected override string GetFieldDeclaration (FieldInfo field)
4015 string name = GetName (field);
4021 protected override string GetEventDeclaration (EventInfo e)
4023 string name = GetName (e);
4030 class FileNameMemberFormatter : SlashDocMemberFormatter {
4031 protected override StringBuilder AppendNamespace (StringBuilder buf, Type type)
4036 protected override char NestedTypeSeparator {