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;
18 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 namespace Mono.Documentation {
35 class MDocUpdaterOptions
41 [Option("The root {directory} of an assembly's documentation files.")]
43 public string path = null;
46 [Option("When updating documentation, write the updated files to this {path}.")]
48 public string updateto = null;
51 [Option(-1, "The assembly to document. Specify a {file} path or the name of a GAC'd assembly.")]
53 public string[] assembly = null;
56 [Option(-1, "Document only the {type name}d by this argument.")]
58 public string[] type = null;
61 [Option("Update only the types in this {namespace}.")]
63 public string @namespace = null;
66 [Option("Allow monodocer to delete members from files.")]
68 public bool delete = false;
71 [Option("Include overridden methods in documentation.")]
73 public bool overrides = false;
76 [Option("Don't update members.")]
78 public bool ignoremembers = false;
81 [Option("Don't rename documentation XML files for missing types. IGNORED.")]
83 public bool ignore_extra_docs = false;
86 [Option("The {name} of the project this documentation is for.")]
91 [Option("An XML documentation {file} made by the /doc option of mcs/csc the contents of which will be imported.")]
93 public string importslashdoc;
96 [Option("An ECMA or monodoc-generated XML documemntation {file} to import.")]
98 public string importecmadoc;
101 [Option("Import either a /doc or ECMA documentation file.")]
103 public string import;
106 [Option("Indent the XML files nicely.")]
108 public bool pretty = false;
111 [Option("Create a <since/> element for added types/members with the value {since}.")]
116 [Option("Show full stack trace on error.")]
118 public bool show_exceptions;
123 static string srcPath;
124 static Assembly[] assemblies;
126 static bool nooverrides = true, delete = false, ignoremembers = false;
127 static bool pretty = false;
128 static bool show_exceptions = false;
130 static int additions = 0, deletions = 0;
133 static XmlDocument slashdocs;
134 static XmlReader ecmadocs;
138 static MemberFormatter csharpFullFormatter = new CSharpFullMemberFormatter ();
139 static MemberFormatter csharpFormatter = new CSharpMemberFormatter ();
140 static MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
141 static MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
142 static MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
144 static MyXmlNodeList extensionMethods = new MyXmlNodeList ();
146 const BindingFlags DefaultBindingFlags =
147 BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly;
150 public static void Main(string[] args)
152 MDocUpdaterOptions opts = new MDocUpdaterOptions ();
153 opts.ProcessArgs(args);
155 if (args.Length == 0) {
163 public static void Run (MDocUpdaterOptions opts)
165 nooverrides = !opts.overrides;
166 delete = opts.delete;
167 ignoremembers = opts.ignoremembers;
169 pretty = opts.pretty;
171 show_exceptions = opts.show_exceptions;
174 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
176 if (opts.path == null)
177 throw new InvalidOperationException("The path option is required.");
181 if (opts.type != null && opts.type.Length > 0 && opts.@namespace != null)
182 throw new InvalidOperationException("You cannot specify both 'type' and 'namespace'.");
184 if (opts.assembly == null)
185 throw new InvalidOperationException("The assembly option is required.");
187 assemblies = new Assembly [opts.assembly.Length];
188 for (int i = 0; i < opts.assembly.Length; i++)
189 assemblies [i] = LoadAssembly (opts.assembly [i]);
193 if (opts.importslashdoc != null) {
195 slashdocs = new XmlDocument();
196 slashdocs.Load(opts.importslashdoc);
197 } catch (Exception e) {
198 Error ("Could not load /doc file: {0}", e.Message);
199 Environment.ExitCode = 1;
204 if (opts.importecmadoc != null) {
206 ecmadocs = new XmlTextReader (opts.importecmadoc);
207 } catch (Exception e) {
208 Error ("Could not load ECMA XML file: {0}", e.Message);
209 Environment.ExitCode = 1;
214 if (opts.import != null && ecmadocs == null && slashdocs == null) {
216 XmlReader r = new XmlTextReader (opts.import);
218 while (r.NodeType != XmlNodeType.Element) {
220 throw new Exception ("Unable to read XML file: " +
223 if (r.LocalName == "doc") {
224 slashdocs = new XmlDocument();
225 slashdocs.Load (opts.import);
227 else if (r.LocalName == "Libraries") {
228 ecmadocs = new XmlTextReader (opts.import);
231 throw new Exception ("Unsupported XML format within " + opts.import);
234 } catch (Exception e) {
235 Error ("Could not load XML file: {0}", e.Message);
236 Environment.ExitCode = 1;
241 // PERFORM THE UPDATES
243 string dest_dir = opts.updateto != null ? opts.updateto : opts.path;
244 if (opts.type != null && opts.type.Length > 0)
245 DoUpdateTypes(opts.path, opts.type, dest_dir);
246 else if (opts.@namespace != null)
247 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
248 Path.Combine (dest_dir, opts.@namespace));
250 DoUpdateAssemblies(opts.path, dest_dir);
252 } catch (InvalidOperationException error) {
253 Error (opts.show_exceptions ? error.ToString () : error.Message);
254 Environment.ExitCode = 1;
257 } catch (System.IO.IOException error) {
258 Error (opts.show_exceptions ? error.ToString () : error.Message);
259 Environment.ExitCode = 1;
262 } catch (Exception error) {
263 Error (opts.show_exceptions ? error.ToString () : error.Message);
264 Environment.ExitCode = 1;
267 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
270 private static void Error (object message)
272 Console.Error.Write ("monodocer: ");
273 Console.Error.WriteLine (message);
276 private static void Error (string format, params object[] args)
278 Console.Error.Write ("monodocer: ");
279 Console.Error.WriteLine (format, args);
282 private static Assembly LoadAssembly (string name)
284 Assembly assembly = null;
286 assembly = Assembly.LoadFile (name);
287 } catch (System.IO.FileNotFoundException) { }
289 if (assembly == null) {
291 assembly = Assembly.LoadWithPartialName (name);
292 } catch (Exception) { }
295 if (assembly == null)
296 throw new InvalidOperationException("Assembly " + name + " not found.");
301 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
302 OrderTypeAttributes (element);
303 XmlTextWriter writer = new XmlTextWriter(output);
304 writer.Formatting = Formatting.Indented;
305 writer.Indentation = 2;
306 writer.IndentChar = ' ';
307 element.WriteTo(writer);
311 private static void OrderTypeAttributes (XmlElement e)
313 foreach (XmlElement type in e.SelectNodes ("//Type")) {
314 OrderTypeAttributes (type.Attributes);
318 static readonly string[] TypeAttributeOrder = {
319 "Name", "FullName", "FullNameSP", "Maintainer"
322 private static void OrderTypeAttributes (XmlAttributeCollection c)
324 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
325 for (int i = 0; i < c.Count; ++i) {
326 XmlAttribute a = c [i];
327 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
328 if (a.Name == TypeAttributeOrder [j]) {
334 for (int i = attrs.Length-1; i >= 0; --i) {
335 XmlAttribute n = attrs [i];
338 XmlAttribute r = null;
339 for (int j = i+1; j < attrs.Length; ++j) {
340 if (attrs [j] != null) {
348 c.InsertBefore (n, r);
352 private static XmlDocument CreateIndexStub() {
353 XmlDocument index = new XmlDocument();
355 XmlElement index_root = index.CreateElement("Overview");
356 index.AppendChild(index_root);
358 if (assemblies.Length == 0)
359 throw new Exception ("No assembly");
361 XmlElement index_assemblies = index.CreateElement("Assemblies");
362 index_root.AppendChild(index_assemblies);
364 XmlElement index_remarks = index.CreateElement("Remarks");
365 index_remarks.InnerText = "To be added.";
366 index_root.AppendChild(index_remarks);
368 XmlElement index_copyright = index.CreateElement("Copyright");
369 index_copyright.InnerText = "To be added.";
370 index_root.AppendChild(index_copyright);
372 XmlElement index_types = index.CreateElement("Types");
373 index_root.AppendChild(index_types);
378 private static void WriteNamespaceStub(string ns, string outdir) {
379 XmlDocument index = new XmlDocument();
381 XmlElement index_root = index.CreateElement("Namespace");
382 index.AppendChild(index_root);
384 index_root.SetAttribute("Name", ns);
386 XmlElement index_docs = index.CreateElement("Docs");
387 index_root.AppendChild(index_docs);
389 XmlElement index_summary = index.CreateElement("summary");
390 index_summary.InnerText = "To be added.";
391 index_docs.AppendChild(index_summary);
393 XmlElement index_remarks = index.CreateElement("remarks");
394 index_remarks.InnerText = "To be added.";
395 index_docs.AppendChild(index_remarks);
397 using (TextWriter writer = OpenWrite (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew)) {
398 WriteXml(index.DocumentElement, writer);
402 public static void DoUpdateTypes(string basepath, string[] typenames, string dest) {
403 ArrayList found = new ArrayList ();
404 foreach (Assembly assembly in assemblies) {
405 foreach (DocsTypeInfo docsTypeInfo in GetTypes (assembly, typenames)) {
406 string relpath = DoUpdateType (docsTypeInfo.Type, basepath, dest, docsTypeInfo.EcmaDocs);
408 found.Add (docsTypeInfo.Type.FullName);
411 StringList notFound = new StringList (typenames.Length);
412 foreach (string typename in typenames)
413 if (!found.Contains (typename))
414 notFound.Add (typename);
415 if (notFound.Count > 0)
416 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", DocUtils.ToStringArray (notFound)));
419 public static string DoUpdateType(Type type, string basepath, string dest, XmlReader ecmaDocsType)
421 if (type.Namespace == null)
422 Error ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
424 if (!IsPublic (type))
427 // Must get the A+B form of the type name.
428 string typename = GetTypeFileName(type);
430 string reltypefile = DocUtils.PathCombine (type.Namespace, typename + ".xml");
431 string typefile = Path.Combine (basepath, reltypefile);
432 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
434 string output = null;
437 } else if (dest == "-") {
440 output = Path.Combine (dest, reltypefile);
445 XmlDocument basefile = new XmlDocument();
446 if (!pretty) basefile.PreserveWhitespace = true;
448 basefile.Load(typefile);
449 } catch (Exception e) {
450 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
453 DoUpdateType2("Updating", basefile, type, output, false, ecmaDocsType);
456 XmlElement td = StubType(type, output, ecmaDocsType);
460 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
463 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
469 private static XPathNavigator SelectSingleNode (XPathNavigator n, string xpath)
472 return n.SelectSingleNode (xpath);
474 XPathNodeIterator i = n.Select (xpath);
475 XPathNavigator r = null;
476 while (i.MoveNext ()) {
483 public static void DoUpdateNS(string ns, string nspath, string outpath) {
484 Hashtable seenTypes = new Hashtable();
485 Assembly assembly = assemblies [0];
487 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
488 XmlDocument basefile = new XmlDocument();
489 if (!pretty) basefile.PreserveWhitespace = true;
490 string typefile = Path.Combine(nspath, file.Name);
492 basefile.Load(typefile);
493 } catch (Exception e) {
494 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
498 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
499 Type type = assembly.GetType(typename, false);
501 Error ("Type no longer in assembly: " + typename);
505 seenTypes[type] = seenTypes;
506 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false, null);
509 // Stub types not in the directory
510 foreach (DocsTypeInfo docsTypeInfo in GetTypes (assembly, null)) {
511 Type type = docsTypeInfo.Type;
512 if (type.Namespace != ns || seenTypes.ContainsKey(type))
515 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"), docsTypeInfo.EcmaDocs);
516 if (td == null) continue;
520 private static string GetTypeFileName(Type type) {
521 return filenameFormatter.GetName (type);
524 public static string GetTypeFileName (string typename)
526 StringBuilder filename = new StringBuilder (typename.Length);
530 for (int i = 0; i < typename.Length; ++i) {
531 char c = typename [i];
540 filename.Append ('`').Append ((numArgs+1).ToString());
555 return filename.ToString ();
559 private static void AddIndexAssembly (Assembly assembly, XmlElement parent)
561 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
562 index_assembly.SetAttribute("Name", assembly.GetName().Name);
563 index_assembly.SetAttribute("Version", assembly.GetName().Version.ToString());
564 MakeAttributes(index_assembly, assembly, true);
565 parent.AppendChild(index_assembly);
568 private static void DoUpdateAssemblies (string source, string dest)
570 string indexfile = dest + "/index.xml";
572 if (System.IO.File.Exists(indexfile)) {
573 index = new XmlDocument();
574 index.Load(indexfile);
577 ClearElement(index.DocumentElement, "Assembly");
578 ClearElement(index.DocumentElement, "Attributes");
580 index = CreateIndexStub();
584 string defaultTitle = "Untitled";
585 if (assemblies.Length == 1)
586 defaultTitle = assemblies[0].GetName().Name;
587 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
589 WriteElementText(index.DocumentElement, "Title", name);
592 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
593 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
594 index_assemblies.RemoveAll ();
596 Hashtable goodfiles = new Hashtable();
598 foreach (Assembly assm in assemblies) {
599 AddIndexAssembly (assm, index_assemblies);
600 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
603 SortIndexEntries (index_types);
605 CleanupFiles (dest, goodfiles);
606 CleanupIndexTypes (index_types, goodfiles);
607 CleanupExtensions (index_types);
609 using (TextWriter writer = OpenWrite (indexfile, FileMode.Create))
610 WriteXml(index.DocumentElement, writer);
613 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
615 private static void DoUpdateAssembly (Assembly assembly, XmlElement index_types, string source, string dest, Hashtable goodfiles)
617 foreach (DocsTypeInfo docTypeInfo in GetTypes (assembly, null)) {
618 Type type = docTypeInfo.Type;
619 if (!IsPublic (type) || type.FullName.IndexOfAny (InvalidFilenameChars) >= 0)
622 string reltypepath = DoUpdateType (type, source, dest, docTypeInfo.EcmaDocs);
623 if (reltypepath == null)
626 // Add namespace and type nodes into the index file as needed
627 XmlElement nsnode = (XmlElement)index_types.SelectSingleNode("Namespace[@Name='" + type.Namespace + "']");
628 if (nsnode == null) {
629 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
630 nsnode.SetAttribute("Name", type.Namespace);
631 index_types.AppendChild(nsnode);
633 string typename = GetTypeFileName(type);
634 string doc_typename = GetDocTypeName (type);
635 XmlElement typenode = (XmlElement)nsnode.SelectSingleNode("Type[@Name='" + typename + "']");
636 if (typenode == null) {
637 typenode = index_types.OwnerDocument.CreateElement("Type");
638 typenode.SetAttribute("Name", typename);
639 nsnode.AppendChild(typenode);
641 if (typename != doc_typename)
642 typenode.SetAttribute("DisplayName", doc_typename);
644 typenode.RemoveAttribute("DisplayName");
645 typenode.SetAttribute ("Kind", GetTypeKind (type));
647 // Ensure the namespace index file exists
648 string onsdoc = DocUtils.PathCombine (dest, type.Namespace + ".xml");
649 string nsdoc = DocUtils.PathCombine (dest, "ns-" + type.Namespace + ".xml");
650 if (File.Exists (onsdoc)) {
651 File.Move (onsdoc, nsdoc);
654 if (!File.Exists (nsdoc)) {
655 Console.WriteLine("New Namespace File: " + type.Namespace);
656 WriteNamespaceStub(type.Namespace, dest);
659 // mark the file as corresponding to a type
660 goodfiles[reltypepath] = goodfiles;
666 public XmlReader EcmaDocs;
668 public DocsTypeInfo (Type type, XmlReader docs)
671 this.EcmaDocs = docs;
679 IEnumerable<Mono.Documentation.MDocUpdater.DocsTypeInfo>
681 GetTypes (Assembly assembly, string[] forTypes)
683 Hashtable seen = null;
684 if (forTypes != null)
685 Array.Sort (forTypes);
686 if (ecmadocs != null) {
687 seen = new Hashtable ();
689 while (ecmadocs.Read ()) {
690 switch (ecmadocs.Name) {
693 typeDepth = ecmadocs.Depth;
694 if (ecmadocs.NodeType != XmlNodeType.Element)
696 if (typeDepth != ecmadocs.Depth) // nested <Type/> element?
698 string typename = ecmadocs.GetAttribute ("FullName");
699 string typename2 = GetTypeFileName (typename);
700 if (forTypes != null &&
701 Array.BinarySearch (forTypes, typename) < 0 &&
702 typename != typename2 &&
703 Array.BinarySearch (forTypes, typename2) < 0)
706 if ((t = assembly.GetType (typename, false)) == null &&
707 (t = assembly.GetType (typename2, false)) == null)
709 seen.Add (typename, "");
710 if (typename != typename2)
711 seen.Add (typename2, "");
712 Console.WriteLine (" Import: {0}", t.FullName);
713 yield return new DocsTypeInfo (t, ecmadocs);
721 foreach (Type type in assembly.GetTypes()) {
722 if (forTypes != null && Array.BinarySearch (forTypes, type.FullName) < 0)
724 if (seen != null && seen.ContainsKey (type.FullName))
726 yield return new DocsTypeInfo (type, null);
730 private static void SortIndexEntries (XmlElement indexTypes)
732 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
733 XmlNodeComparer c = new AttributeNameComparer ();
734 SortXmlNodes (indexTypes, namespaces, c);
736 for (int i = 0; i < namespaces.Count; ++i)
737 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
740 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
742 MyXmlNodeList l = new MyXmlNodeList (children.Count);
743 for (int i = 0; i < children.Count; ++i)
744 l.Add (children [i]);
746 for (int i = l.Count - 1; i > 0; --i) {
747 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
751 abstract class XmlNodeComparer : IComparer
756 public abstract int Compare (XmlNode x, XmlNode y);
758 public int Compare (object x, object y)
760 return Compare ((XmlNode) x, (XmlNode) y);
764 class AttributeNameComparer : XmlNodeComparer {
765 public override int Compare (XmlNode x, XmlNode y)
767 return x.Attributes ["Name"].Value.CompareTo (y.Attributes ["Name"].Value);
771 class VersionComparer : XmlNodeComparer {
772 public override int Compare (XmlNode x, XmlNode y)
774 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
775 string a = GetVersion (x.InnerText);
776 string b = GetVersion (y.InnerText);
777 return new Version (a).CompareTo (new Version (b));
780 static string GetVersion (string v)
782 int n = v.IndexOf ("x");
785 return v.Substring (0, n-1);
789 private static string GetTypeKind (Type type)
792 return "Enumeration";
793 if (type.IsValueType)
795 if (type.IsInterface)
797 if (IsDelegate (type))
799 if (type.IsClass || type == typeof(System.Enum))
801 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
804 private static bool IsPublic (Type type)
807 while (decl != null) {
808 if (!(decl.IsPublic || decl.IsNestedPublic)) {
811 decl = decl.DeclaringType;
816 private static void CleanupFiles (string dest, Hashtable goodfiles)
818 // Look for files that no longer correspond to types
819 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
820 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
821 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
822 if (!goodfiles.ContainsKey(relTypeFile)) {
823 XmlDocument doc = new XmlDocument ();
824 doc.Load (typefile.FullName);
825 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
826 if (UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
827 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
828 WriteXml(doc.DocumentElement, writer);
829 goodfiles [relTypeFile] = goodfiles;
832 string newname = typefile.FullName + ".remove";
833 try { System.IO.File.Delete(newname); } catch (Exception) { }
834 try { typefile.MoveTo(newname); } catch (Exception) { }
835 Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
841 private static TextWriter OpenWrite (string path, FileMode mode)
843 return new StreamWriter (
844 new FileStream (path, mode),
845 new UTF8Encoding (false)
849 private static string[] GetAssemblyVersions ()
851 StringList versions = new StringList (assemblies.Length);
852 for (int i = 0; i < assemblies.Length; ++i)
853 versions.Add (GetAssemblyVersion (assemblies [i]));
854 return DocUtils.ToStringArray (versions);
857 private static void CleanupIndexTypes (XmlElement index_types, Hashtable goodfiles)
859 // Look for type nodes that no longer correspond to types
860 MyXmlNodeList remove = new MyXmlNodeList ();
861 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
862 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
863 if (!goodfiles.ContainsKey(fulltypename)) {
864 remove.Add (typenode);
867 foreach (XmlNode n in remove)
868 n.ParentNode.RemoveChild (n);
871 private static void CleanupExtensions (XmlElement index_types)
873 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
874 if (extensionMethods.Count == 0) {
877 index_types.RemoveChild (e);
881 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
882 index_types.SelectSingleNode ("/Overview").AppendChild (e);
886 extensionMethods.Sort (DefaultExtensionMethodComparer);
887 foreach (XmlNode m in extensionMethods) {
888 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
892 class ExtensionMethodComparer : XmlNodeComparer {
893 public override int Compare (XmlNode x, XmlNode y)
895 XmlNode xLink = x.SelectSingleNode ("Member/Link");
896 XmlNode yLink = y.SelectSingleNode ("Member/Link");
898 int n = xLink.Attributes ["Type"].Value.CompareTo (
899 yLink.Attributes ["Type"].Value);
902 n = xLink.Attributes ["Member"].Value.CompareTo (
903 yLink.Attributes ["Member"].Value);
904 if (n == 0 && !object.ReferenceEquals (x, y))
905 throw new InvalidOperationException ("Duplicate extension method found!");
910 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
912 public static void DoUpdateType2(string message, XmlDocument basefile, Type type, string output, bool insertSince, XmlReader ecmaDocsType) {
913 Console.WriteLine(message + ": " + type.FullName);
915 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
917 // Update type metadata
918 UpdateType(basefile.DocumentElement, type, ecmaDocsType);
920 if (ecmaDocsType != null) {
921 while (ecmaDocsType.Name != "Members" && ecmaDocsType.Read ()) {
924 if (ecmaDocsType.IsEmptyElement)
928 // Update existing members. Delete member nodes that no longer should be there,
929 // and remember what members are already documented so we don't add them again.
930 if (!ignoremembers) {
931 MyXmlNodeList todelete = new MyXmlNodeList ();
932 foreach (DocsNodeInfo info in GetDocumentationMembers (basefile, type, ecmaDocsType)) {
933 XmlElement oldmember = info.Node;
934 MemberInfo oldmember2 = info.Member;
935 string sig = oldmember2 != null ? MakeMemberSignature(oldmember2) : null;
937 // Interface implementations and overrides are deleted from the docs
938 // unless the overrides option is given.
939 if (oldmember2 != null && (!IsNew(oldmember2) || sig == null))
942 // Deleted (or signature changed)
943 if (oldmember2 == null) {
944 if (UpdateAssemblyVersions (oldmember, new string[]{GetAssemblyVersion (type.Assembly)}, false))
946 DeleteMember ("Member Removed", output, oldmember, todelete);
951 if (seenmembers.ContainsKey (sig)) {
952 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
953 // ignore, already seen
955 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
956 DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
958 Error ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
962 // Update signature information
965 seenmembers.Add (sig, oldmember);
967 foreach (XmlElement oldmember in todelete)
968 oldmember.ParentNode.RemoveChild (oldmember);
971 if (!IsDelegate(type) && !ignoremembers) {
972 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
973 foreach (MemberInfo m in Sort (type.GetMembers(DefaultBindingFlags))) {
974 if (m is Type) continue;
976 string sig = MakeMemberSignature(m);
977 if (sig == null) continue;
978 if (seenmembers.ContainsKey(sig)) continue;
980 // To be nice on diffs, members/properties/events that are overrides or are interface implementations
982 if (!IsNew(m)) continue;
984 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
985 if (mm == null) continue;
986 members.AppendChild( mm );
988 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
993 // Import code snippets from files
994 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
995 if (!(code is XmlElement)) continue;
996 string file = ((XmlElement)code).GetAttribute("src");
997 string lang = ((XmlElement)code).GetAttribute("lang");
999 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
1001 code.InnerText = src;
1005 if (insertSince && since != null) {
1006 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
1007 docs.AppendChild (CreateSinceNode (basefile));
1011 XmlElement d = basefile.DocumentElement ["Docs"];
1012 XmlElement m = basefile.DocumentElement ["Members"];
1013 if (d != null && m != null)
1014 basefile.DocumentElement.InsertBefore (
1015 basefile.DocumentElement.RemoveChild (d), m);
1016 SortTypeMembers (m);
1019 System.IO.TextWriter writer;
1021 writer = Console.Out;
1023 FileInfo file = new FileInfo (output);
1024 if (!file.Directory.Exists) {
1025 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
1026 file.Directory.Create ();
1028 writer = OpenWrite (output, FileMode.Create);
1032 WriteXml(basefile.DocumentElement, writer);
1035 private static string GetCodeSource (string lang, string file)
1038 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
1039 // Grab the specified region
1040 string region = "#region " + file.Substring (anchorStart + 4);
1041 file = file.Substring (0, anchorStart + 3);
1043 using (StreamReader reader = new StreamReader (file)) {
1045 StringBuilder src = new StringBuilder ();
1047 while ((line = reader.ReadLine ()) != null) {
1048 if (line.Trim() == region) {
1049 indent = line.IndexOf (region);
1052 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
1057 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
1060 return src.ToString ();
1062 } catch (Exception e) {
1063 Error ("Could not load <code/> file '{0}' region '{1}': {2}",
1064 file, region, show_exceptions ? e.ToString () : e.Message);
1069 using (StreamReader reader = new StreamReader (file))
1070 return reader.ReadToEnd ();
1071 } catch (Exception e) {
1072 Error ("Could not load <code/> file '" + file + "': " + e.Message);
1081 IEnumerable<DocsNodeInfo>
1083 GetDocumentationMembers (XmlDocument basefile, Type type, XmlReader ecmaDocsMembers)
1085 if (ecmaDocsMembers != null) {
1086 int membersDepth = ecmaDocsMembers.Depth;
1088 while (go && ecmaDocsMembers.Read ()) {
1089 switch (ecmaDocsMembers.Name) {
1091 if (membersDepth != ecmaDocsMembers.Depth - 1 || ecmaDocsMembers.NodeType != XmlNodeType.Element)
1093 DocumentationMember dm = new DocumentationMember (ecmaDocsMembers);
1094 string xp = GetXPathForMember (dm);
1095 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
1097 if (oldmember == null) {
1098 m = GetMember (type, dm);
1100 Error ("Could not import ECMA docs for `{0}'s `{1}': MemberInfo not found.",
1101 type.FullName, dm.MemberSignatures ["C#"]);
1102 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
1105 // oldmember lookup may have failed due to type parameter renames.
1107 oldmember = (XmlElement) basefile.SelectSingleNode (GetXPathForMember (m));
1108 if (oldmember == null) {
1109 XmlElement members = WriteElement(basefile.DocumentElement, "Members");
1110 oldmember = basefile.CreateElement ("Member");
1111 oldmember.SetAttribute ("MemberName", dm.MemberName);
1112 members.AppendChild (oldmember);
1113 foreach (string key in Sort (dm.MemberSignatures.Keys)) {
1114 XmlElement ms = basefile.CreateElement ("MemberSignature");
1115 ms.SetAttribute ("Language", key);
1116 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
1117 oldmember.AppendChild (ms);
1119 oldmember.SetAttribute ("__monodocer-seen__", "true");
1120 Console.WriteLine ("Member Added: {0}", MakeMemberSignature (m));
1125 m = GetMember (type, new DocumentationMember (oldmember));
1127 Error ("Could not import ECMA docs for `{0}'s `{1}': MemberInfo not found.",
1128 type.FullName, dm.MemberSignatures ["C#"]);
1131 oldmember.SetAttribute ("__monodocer-seen__", "true");
1133 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
1134 if (ecmaDocsMembers.Name != "Docs")
1135 throw new InvalidOperationException ("Found " + ecmaDocsMembers.Name + "; expected <Docs/>!");
1136 node.EcmaDocs = ecmaDocsMembers;
1141 if (membersDepth == ecmaDocsMembers.Depth && ecmaDocsMembers.NodeType == XmlNodeType.EndElement) {
1148 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
1149 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
1150 oldmember.RemoveAttribute ("__monodocer-seen__");
1153 MemberInfo m = GetMember (type, new DocumentationMember (oldmember));
1155 yield return new DocsNodeInfo (oldmember);
1158 yield return new DocsNodeInfo (oldmember, m);
1163 static void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
1165 string format = output != null
1166 ? "{0}: File='{1}'; Signature='{4}'"
1167 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1171 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1172 member.Attributes ["MemberName"].Value,
1173 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
1174 if (!delete && MemberDocsHaveUserContent (member)) {
1175 Error ("Member deletions must be enabled with the --delete option.");
1177 todelete.Add (member);
1182 class MemberComparer : XmlNodeComparer {
1183 public override int Compare (XmlNode x, XmlNode y)
1186 string xMemberName = x.Attributes ["MemberName"].Value;
1187 string yMemberName = y.Attributes ["MemberName"].Value;
1189 // generic methods *end* with '>'
1190 // it's possible for explicitly implemented generic interfaces to
1191 // contain <...> without being a generic method
1192 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1193 (r = xMemberName.CompareTo (yMemberName)) != 0)
1197 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1198 xMemberName = xMemberName.Substring (0, lt);
1199 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1200 yMemberName = yMemberName.Substring (0, lt);
1201 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1204 // if @MemberName matches, then it's either two different types of
1205 // members sharing the same name, e.g. field & property, or it's an
1206 // overloaded method.
1207 // for different type, sort based on MemberType value.
1208 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1209 y.SelectSingleNode ("MemberType").InnerText);
1213 // same type -- must be an overloaded method. Sort based on type
1214 // parameter count, then parameter count, then by the parameter
1216 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1217 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1218 if (xTypeParams.Count != yTypeParams.Count)
1219 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1220 for (int i = 0; i < xTypeParams.Count; ++i) {
1221 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1222 yTypeParams [i].Attributes ["Name"].Value);
1227 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1228 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1229 if (xParams.Count != yParams.Count)
1230 return xParams.Count <= yParams.Count ? -1 : 1;
1231 for (int i = 0; i < xParams.Count; ++i) {
1232 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1233 yParams [i].Attributes ["Type"].Value);
1237 // all parameters match, but return value might not match if it was
1238 // changed between one version and another.
1239 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1240 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1241 if (xReturn != null && yReturn != null) {
1242 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1251 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1253 private static void SortTypeMembers (XmlNode members)
1255 if (members == null)
1257 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1260 private static bool MemberDocsHaveUserContent (XmlNode e)
1262 e = (XmlElement)e.SelectSingleNode("Docs");
1263 if (e == null) return false;
1264 foreach (XmlElement d in e.SelectNodes("*"))
1265 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1270 private static bool IsNew(MemberInfo m) {
1271 if (!nooverrides) return true;
1272 if (m is MethodInfo && !IsNew((MethodInfo)m)) return false;
1273 if (m is PropertyInfo && !IsNew(((PropertyInfo)m).GetGetMethod())) return false;
1274 if (m is PropertyInfo && !IsNew(((PropertyInfo)m).GetSetMethod())) return false;
1275 if (m is EventInfo && !IsNew(((EventInfo)m).GetAddMethod(true))) return false;
1276 if (m is EventInfo && !IsNew(((EventInfo)m).GetRaiseMethod())) return false;
1277 if (m is EventInfo && !IsNew(((EventInfo)m).GetRemoveMethod())) return false;
1281 private static bool IsNew(MethodInfo m) {
1282 if (m == null) return true;
1283 MethodInfo b = m.GetBaseDefinition();
1284 if (b == null || b == m) return true;
1288 // UPDATE HELPER FUNCTIONS
1291 private static XmlElement FindMatchingMember(Type type, XmlElement newfile, XmlElement oldmember) {
1292 MemberInfo oldmember2 = GetMember(type, oldmember.CreateNavigator ());
1293 if (oldmember2 == null) return null;
1295 string membername = oldmember.GetAttribute("MemberName");
1296 foreach (XmlElement newmember in newfile.SelectNodes("Members/Member[@MemberName='" + membername + "']")) {
1297 if (GetMember(type, newmember.CreateNavigator ()) == oldmember2) return newmember;
1304 private static MemberInfo GetMember(Type type, DocumentationMember member) {
1305 string membertype = member.MemberType;
1307 string returntype = member.ReturnType;
1309 string docName = member.MemberName;
1310 string[] docTypeParams = GetTypeParameters (docName);
1312 // Loop through all members in this type with the same name
1313 foreach (MemberInfo mi in GetReflectionMembers (type, docName)) {
1314 if (mi is Type) continue;
1315 if (GetMemberType(mi) != membertype) continue;
1317 string sig = MakeMemberSignature(mi);
1318 if (sig == null) continue; // not publicly visible
1320 ParameterInfo[] pis = null;
1321 string[] typeParams = null;
1322 if (mi is MethodInfo || mi is ConstructorInfo) {
1323 MethodBase mb = (MethodBase) mi;
1324 pis = mb.GetParameters();
1325 if (docTypeParams != null && DocUtils.GetContainsGenericParameters (mb)) {
1326 Type[] args = DocUtils.GetGenericArguments (mb);
1327 if (args.Length == docTypeParams.Length) {
1328 typeParams = new string [args.Length];
1329 for (int i = 0; i < args.Length; ++i)
1330 typeParams [i] = args [i].Name;
1334 else if (mi is PropertyInfo)
1335 pis = ((PropertyInfo)mi).GetIndexParameters();
1337 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
1338 int pcount = pis == null ? 0 : pis.Length;
1339 if (mcount != pcount)
1342 if (mi is MethodInfo) {
1343 // Casting operators can overload based on return type.
1344 if (returntype != GetReplacedString (
1345 GetDocTypeFullName (((MethodInfo)mi).ReturnType),
1346 typeParams, docTypeParams)) {
1354 for (int i = 0; i < pis.Length; i++) {
1355 string paramType = GetReplacedString (
1356 GetDocParameterType (pis [i].ParameterType),
1357 typeParams, docTypeParams);
1358 if (paramType != (string) member.Parameters [i]) {
1363 if (!good) continue;
1371 private static MemberInfoEnumerable GetReflectionMembers (Type type, string docName)
1373 // need to worry about 4 forms of //@MemberName values:
1374 // 1. "Normal" (non-generic) member names: GetEnumerator
1376 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
1377 // - try as-is, and try type.member (due to "kludge" for property
1379 // 3. "Normal" Generic member names: Sort<T> (CSC)
1380 // - need to remove generic parameters --> "Sort"
1381 // 4. Explicitly-implemented interface members for generic interfaces:
1382 // -- System.Collections.Generic.IEnumerable<T>.Current
1383 // - Try as-is, and try type.member, *keeping* the generic parameters.
1384 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
1385 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
1386 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
1387 // this as (1) or (2).
1388 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
1390 foreach (MemberInfo mi in type.GetMember (docName, DefaultBindingFlags))
1392 if (CountChars (docName, '.') > 0)
1393 // might be a property; try only type.member instead of
1394 // namespace.type.member.
1395 foreach (MemberInfo mi in
1396 type.GetMember (DocUtils.GetTypeDotMember (docName), DefaultBindingFlags))
1403 int startLt, startType, startMethod;
1404 startLt = startType = startMethod = -1;
1405 for (int i = 0; i < docName.Length; ++i) {
1406 switch (docName [i]) {
1415 if (numLt == 0 && (i + 1) < docName.Length)
1416 // there's another character in docName, so this <...> sequence is
1417 // probably part of a generic type -- case 4.
1421 startType = startMethod;
1427 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
1429 foreach (MemberInfo mi in type.GetMember (refName, DefaultBindingFlags))
1433 foreach (MemberInfo mi in type.GetMember (refName.Substring (startType + 1), DefaultBindingFlags))
1436 // If we _still_ haven't found it, we've hit another generic naming issue:
1437 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
1438 // explicitly-implemented METHOD names (not properties), e.g.
1439 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
1440 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
1441 // which the XML docs will contain.
1443 // Alas, we can't derive the Mono name from docName, so we need to iterate
1444 // over all member names, convert them into CSC format, and compare... :-(
1447 foreach (MemberInfo mi in type.GetMembers (DefaultBindingFlags)) {
1448 if (GetMemberName (mi) == docName)
1453 static string[] GetTypeParameters (string docName)
1455 if (docName [docName.Length-1] != '>')
1457 StringList types = new StringList ();
1458 int endToken = docName.Length-2;
1459 int i = docName.Length-2;
1461 if (docName [i] == ',' || docName [i] == '<') {
1462 types.Add (docName.Substring (i + 1, endToken - i));
1465 if (docName [i] == '<')
1470 return DocUtils.ToStringArray (types);
1473 static string GetReplacedString (string typeName, string[] from, string[] to)
1477 for (int i = 0; i < from.Length; ++i)
1478 typeName = typeName.Replace (from [i], to [i]);
1482 // CREATE A STUB DOCUMENTATION FILE
1484 public static XmlElement StubType(Type type, string output, XmlReader ecmaDocsType) {
1485 string typesig = MakeTypeSignature(type);
1486 if (typesig == null) return null; // not publicly visible
1488 XmlDocument doc = new XmlDocument();
1489 XmlElement root = doc.CreateElement("Type");
1490 doc.AppendChild (root);
1492 DoUpdateType2 ("New Type", doc, type, output, true, ecmaDocsType);
1497 private static XmlElement CreateSinceNode (XmlDocument doc)
1499 XmlElement s = doc.CreateElement ("since");
1500 s.SetAttribute ("version", since);
1504 // STUBBING/UPDATING FUNCTIONS
1506 public static void UpdateType(XmlElement root, Type type, XmlReader ecmaDocsType) {
1507 root.SetAttribute("Name", GetDocTypeName (type));
1508 root.SetAttribute("FullName", GetDocTypeFullName (type));
1510 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Language", "C#");
1511 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Value", MakeTypeSignature(type));
1513 XmlElement ass = WriteElement(root, "AssemblyInfo");
1514 WriteElementText(ass, "AssemblyName", type.Assembly.GetName().Name);
1515 UpdateAssemblyVersions(root, type, true);
1516 if (type.Assembly.GetName().CultureInfo.Name != "")
1517 WriteElementText(ass, "AssemblyCulture", type.Assembly.GetName().CultureInfo.Name);
1519 ClearElement(ass, "AssemblyCulture");
1521 // Why-oh-why do we put assembly attributes in each type file?
1522 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1523 // since they're outdated in current docs, and a waste of space.
1524 //MakeAttributes(ass, type.Assembly, true);
1525 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1526 if (assattrs != null)
1527 ass.RemoveChild(assattrs);
1529 NormalizeWhitespace(ass);
1531 if (DocUtils.IsGenericType (type)) {
1532 MakeTypeParameters (root, DocUtils.GetGenericArguments (type));
1534 ClearElement(root, "TypeParameters");
1537 if (type.BaseType != null) {
1538 XmlElement basenode = WriteElement(root, "Base");
1540 string basetypename = GetDocTypeFullName (type.BaseType);
1541 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1542 WriteElementText(root, "Base/BaseTypeName", basetypename);
1544 // Document how this type instantiates the generic parameters of its base type
1545 if (DocUtils.IsGenericType (type.BaseType)) {
1546 ClearElement(basenode, "BaseTypeArguments");
1547 Type[] baseGenArgs = DocUtils.GetGenericArguments (type.BaseType);
1548 Type genericDefinition = DocUtils.GetGenericTypeDefinition (type.BaseType);
1549 Type[] genTypeDefArgs = DocUtils.GetGenericArguments (genericDefinition);
1550 for (int i = 0; i < baseGenArgs.Length; i++) {
1551 Type typearg = baseGenArgs [i];
1552 Type typeparam = genTypeDefArgs [i];
1554 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1555 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1556 bta.AppendChild(arg);
1557 arg.SetAttribute("TypeParamName", typeparam.Name);
1558 arg.InnerText = GetDocTypeFullName (typearg);
1562 ClearElement(root, "Base");
1565 if (!IsDelegate(type) && !type.IsEnum) {
1566 // Get a sorted list of interface implementations. Don't include
1567 // interfaces that are implemented by a base type or another interface
1568 // because they go on the base type or base interface's signature.
1569 ArrayList interface_names = new ArrayList();
1570 foreach (Type i in type.GetInterfaces())
1571 if ((type.BaseType == null || Array.IndexOf(type.BaseType.GetInterfaces(), i) == -1) && InterfaceNotFromAnother(i, type.GetInterfaces()))
1572 interface_names.Add(GetDocTypeFullName (i));
1573 interface_names.Sort();
1575 XmlElement interfaces = WriteElement(root, "Interfaces");
1576 interfaces.RemoveAll();
1577 foreach (string iname in interface_names) {
1578 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1579 interfaces.AppendChild(iface);
1580 WriteElementText(iface, "InterfaceName", iname);
1583 ClearElement(root, "Interfaces");
1586 MakeAttributes(root, type, false);
1588 if (IsDelegate(type)) {
1589 MakeTypeParameters (root, DocUtils.GetGenericArguments (type));
1590 MakeParameters(root, type.GetMethod("Invoke").GetParameters());
1591 MakeReturnValue(root, type.GetMethod("Invoke"));
1594 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1595 if (ecmaDocsType != null) {
1596 if (ecmaDocsType.Name != "Docs") {
1597 int depth = ecmaDocsType.Depth;
1598 while (ecmaDocsType.Read ()) {
1599 if (ecmaDocsType.Name == "Docs" && ecmaDocsType.Depth == depth + 1)
1603 if (!ecmaDocsType.IsStartElement ("Docs"))
1604 throw new InvalidOperationException ("Found " + ecmaDocsType.Name + "; expecting <Docs/>!");
1605 typeInfo.EcmaDocs = ecmaDocsType;
1607 MakeDocNode (typeInfo);
1609 if (!IsDelegate (type))
1610 WriteElement (root, "Members");
1612 NormalizeWhitespace(root);
1615 class MemberInfoComparer : IComparer
1617 , IComparer<MemberInfo>
1620 public int Compare (MemberInfo x, MemberInfo y)
1622 string xs = slashdocFormatter.GetName (x);
1623 string ys = slashdocFormatter.GetName (y);
1624 // return String.Compare (xs, ys, StringComparison.OrdinalIgnoreCase);
1625 return string.Compare (xs, ys, true, CultureInfo.InvariantCulture);
1628 public int Compare (object x, object y)
1630 return Compare ((MemberInfo) x, (MemberInfo) y);
1634 static MemberInfoComparer memberInfoComparer = new MemberInfoComparer ();
1636 private static MemberInfo[] Sort (MemberInfo[] members)
1639 ArrayList l = new ArrayList ();
1640 l.AddRange (members);
1641 l.Sort (memberInfoComparer);
1642 return (MemberInfo[]) l.ToArray (typeof(MemberInfo));
1644 Array.Sort (members, memberInfoComparer);
1649 static IEnumerable Sort (IEnumerable list)
1651 ArrayList l = new ArrayList (list as ICollection);
1657 static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1659 List<T> l = new List<T> (list);
1665 private static void UpdateMember(DocsNodeInfo info) {
1666 XmlElement me = (XmlElement) info.Node;
1667 MemberInfo mi = info.Member;
1668 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Language", "C#");
1669 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Value", MakeMemberSignature(mi));
1671 WriteElementText(me, "MemberType", GetMemberType(mi));
1673 UpdateAssemblyVersions(me, mi, true);
1674 MakeAttributes(me, mi, false);
1675 MakeReturnValue(me, mi);
1676 if (mi is MethodBase) {
1677 MethodBase mb = (MethodBase) mi;
1678 if (DocUtils.GetContainsGenericParameters (mb))
1679 MakeTypeParameters (me, DocUtils.GetGenericArguments (mb));
1681 MakeParameters(me, mi);
1684 if (mi is FieldInfo && GetFieldConstValue((FieldInfo)mi, out fieldValue))
1685 WriteElementText(me, "MemberValue", fieldValue);
1687 info.Node = WriteElement (me, "Docs");
1689 UpdateExtensionMethods (me, info);
1692 static readonly string[] ValidExtensionMembers = {
1701 static readonly string[] ValidExtensionDocMembers = {
1707 private static void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1709 MethodInfo me = info.Member as MethodInfo;
1712 if (info.Parameters.Length < 1)
1714 if (!DocUtils.IsExtensionMethod (me))
1717 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1718 XmlNode member = e.CloneNode (true);
1719 em.AppendChild (member);
1720 RemoveExcept (member, ValidExtensionMembers);
1721 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1722 WriteElementText (member, "MemberType", "ExtensionMethod");
1723 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1724 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1725 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1726 member.AppendChild (link);
1727 AddTargets (em, info);
1729 extensionMethods.Add (em);
1732 private static void RemoveExcept (XmlNode node, string[] except)
1736 MyXmlNodeList remove = null;
1737 foreach (XmlNode n in node.ChildNodes) {
1738 if (Array.BinarySearch (except, n.Name) < 0) {
1740 remove = new MyXmlNodeList ();
1745 foreach (XmlNode n in remove)
1746 node.RemoveChild (n);
1749 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1751 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1752 member.PrependChild (targets);
1753 if (!DocUtils.IsGenericParameter (info.Parameters [0].ParameterType))
1754 AppendElementAttributeText (targets, "Target", "Type",
1755 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1757 Type[] constraints = DocUtils.GetGenericParameterConstraints (
1758 info.Parameters [0].ParameterType);
1759 if (constraints.Length == 0)
1760 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1762 foreach (Type c in constraints)
1763 AppendElementAttributeText(targets, "Target", "Type",
1764 slashdocFormatter.GetDeclaration (c));
1768 private static bool GetFieldConstValue(FieldInfo field, out string value) {
1770 if (field.DeclaringType.IsEnum) return false;
1771 if (DocUtils.IsGenericType (field.DeclaringType)) return false;
1772 if (field.IsLiteral || (field.IsStatic && field.IsInitOnly)) {
1775 val = field.GetValue(null);
1779 if (val == null) value = "null";
1780 else if (val is Enum) value = val.ToString();
1781 else if (val is IFormattable) {
1782 value = ((IFormattable)val).ToString();
1784 value = "\"" + value + "\"";
1786 if (value != null && value != "")
1792 // XML HELPER FUNCTIONS
1794 private static XmlElement WriteElement(XmlNode parent, string element) {
1795 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1797 string[] path = element.Split('/');
1798 foreach (string p in path) {
1799 ret = (XmlElement)parent.SelectSingleNode(p);
1802 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1803 ename = ename.Substring(0, ename.IndexOf('['));
1804 ret = parent.OwnerDocument.CreateElement(ename);
1805 parent.AppendChild(ret);
1814 private static void WriteElementText(XmlNode parent, string element, string value) {
1815 XmlElement node = WriteElement(parent, element);
1816 node.InnerText = value;
1819 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1821 XmlElement n = parent.OwnerDocument.CreateElement (element);
1822 parent.AppendChild (n);
1823 n.InnerText = value;
1827 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1829 XmlElement n = parent.OwnerDocument.CreateElement (element);
1830 parent.AppendChild (n);
1831 n.SetAttribute (attribute, value);
1835 private static XmlNode CopyNode (XmlNode source, XmlNode dest)
1837 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1838 dest.AppendChild (copy);
1842 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1843 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1846 node = WriteElement(parent, element);
1847 node.InnerText = value;
1849 private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
1850 XmlElement node = WriteElement(parent, element);
1851 if (node.GetAttribute(attribute) == value) return;
1852 node.SetAttribute(attribute, value);
1854 private static void ClearElement(XmlElement parent, string name) {
1855 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1857 parent.RemoveChild(node);
1859 private static void ClearElementChildren(XmlElement parent) {
1863 // DOCUMENTATION HELPER FUNCTIONS
1865 private static void MakeDocNode (DocsNodeInfo info)
1867 Type[] genericParams = info.GenericParameters;
1868 ParameterInfo[] parameters = info.Parameters;
1869 Type returntype = info.ReturnType;
1870 bool returnisreturn = info.ReturnIsReturn;
1871 XmlElement e = info.Node;
1872 bool addremarks = info.AddRemarks;
1874 WriteElementInitialText(e, "summary", "To be added.");
1876 if (parameters != null) {
1877 string[] values = new string [parameters.Length];
1878 for (int i = 0; i < values.Length; ++i)
1879 values [i] = parameters [i].Name;
1880 UpdateParameters (e, "param", values);
1883 if (genericParams != null) {
1884 string[] values = new string [genericParams.Length];
1885 for (int i = 0; i < values.Length; ++i)
1886 values [i] = genericParams [i].Name;
1887 UpdateParameters (e, "typeparam", values);
1890 string retnodename = null;
1891 if (returntype != null && returntype != typeof(void)) {
1892 retnodename = returnisreturn ? "returns" : "value";
1893 string retnodename_other = !returnisreturn ? "returns" : "value";
1895 // If it has a returns node instead of a value node, change its name.
1896 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1897 if (retother != null) {
1898 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1899 foreach (XmlNode node in retother)
1900 retnode.AppendChild(node.CloneNode(true));
1901 e.ReplaceChild(retnode, retother);
1903 WriteElementInitialText(e, retnodename, "To be added.");
1906 ClearElement(e, "returns");
1907 ClearElement(e, "value");
1911 WriteElementInitialText(e, "remarks", "To be added.");
1913 if (info.EcmaDocs != null) {
1914 XmlReader r = info.EcmaDocs;
1915 int depth = r.Depth;
1916 r.ReadStartElement ("Docs");
1918 if (r.Name == "Docs") {
1919 if (r.Depth == depth && r.NodeType == XmlNodeType.EndElement)
1922 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
1924 if (!r.IsStartElement ())
1929 XmlNode doc = e.SelectSingleNode (
1930 r.Name + "[@name='" + r.GetAttribute ("name") + "']");
1931 string value = r.ReadInnerXml ();
1933 doc.InnerXml = value.Replace ("\r", "");
1940 string name = r.Name;
1941 string cref = r.GetAttribute ("cref");
1942 XmlNode doc = e.SelectSingleNode (
1943 r.Name + "[@cref='" + cref + "']");
1944 string value = r.ReadInnerXml ().Replace ("\r", "");
1946 doc.InnerXml = value;
1948 XmlElement n = e.OwnerDocument.CreateElement (name);
1949 n.SetAttribute ("cref", cref);
1956 string name = r.Name;
1957 string xpath = r.Name;
1958 StringList attributes = new StringList (r.AttributeCount);
1959 if (r.MoveToFirstAttribute ()) {
1961 attributes.Add ("@" + r.Name + "=\"" + r.Value + "\"");
1962 } while (r.MoveToNextAttribute ());
1965 if (attributes.Count > 0) {
1966 xpath += "[" + string.Join (" and ", DocUtils.ToStringArray (attributes)) + "]";
1968 XmlNode doc = e.SelectSingleNode (xpath);
1969 string value = r.ReadInnerXml ().Replace ("\r", "");
1971 doc.InnerXml = value;
1974 XmlElement n = e.OwnerDocument.CreateElement (name);
1976 foreach (string a in attributes) {
1977 int eq = a.IndexOf ('=');
1978 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
1987 if (info.SlashDocs != null) {
1988 XmlNode elem = info.SlashDocs;
1990 if (elem.SelectSingleNode("summary") != null)
1991 ClearElement(e, "summary");
1992 if (elem.SelectSingleNode("remarks") != null)
1993 ClearElement(e, "remarks");
1994 if (elem.SelectSingleNode("value") != null)
1995 ClearElement(e, "value");
1996 if (retnodename != null && elem.SelectSingleNode(retnodename) != null)
1997 ClearElement(e, retnodename);
1999 foreach (XmlNode child in elem.ChildNodes) {
2000 switch (child.Name) {
2003 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + child.Attributes ["name"].Value + "']");
2005 p2.InnerXml = child.InnerXml;
2010 case "permission": {
2011 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + child.Attributes ["cref"].Value + "']");
2013 a = e.OwnerDocument.CreateElement (child.Name);
2014 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
2017 a.InnerXml = child.InnerXml;
2021 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + child.Attributes ["cref"].Value + "']");
2023 a = e.OwnerDocument.CreateElement ("altmember");
2024 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
2030 CopyNode (child, e);
2037 OrderDocsNodes (e, e.ChildNodes);
2038 NormalizeWhitespace(e);
2041 static readonly string[] DocsNodeOrder = {
2042 "typeparam", "param", "summary", "returns", "value", "remarks",
2045 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
2047 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
2048 for (int i = 0; i < DocsNodeOrder.Length; ++i) {
2049 for (int j = 0; j < children.Count; ++j) {
2050 XmlNode c = children [j];
2051 if (c.Name == DocsNodeOrder [i]) {
2052 newChildren.Add (c);
2056 if (newChildren.Count >= 0)
2057 docs.PrependChild ((XmlNode) newChildren [0]);
2058 for (int i = 1; i < newChildren.Count; ++i) {
2059 XmlNode prev = (XmlNode) newChildren [i-1];
2060 XmlNode cur = (XmlNode) newChildren [i];
2061 docs.RemoveChild (cur);
2062 docs.InsertAfter (cur, prev);
2067 private static void UpdateParameters (XmlElement e, string element, string[] values)
2069 if (values != null) {
2070 XmlNode[] paramnodes = new XmlNode[values.Length];
2072 // Some documentation had param nodes with leading spaces.
2073 foreach (XmlElement paramnode in e.SelectNodes(element)){
2074 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
2077 // If a member has only one parameter, we can track changes to
2078 // the name of the parameter easily.
2079 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
2080 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
2083 bool reinsert = false;
2085 // Pick out existing and still-valid param nodes, and
2086 // create nodes for parameters not in the file.
2087 Hashtable seenParams = new Hashtable();
2088 for (int pi = 0; pi < values.Length; pi++) {
2089 string p = values [pi];
2092 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
2093 if (paramnodes[pi] != null) continue;
2095 XmlElement pe = e.OwnerDocument.CreateElement(element);
2096 pe.SetAttribute("name", p);
2097 pe.InnerText = "To be added.";
2098 paramnodes[pi] = pe;
2102 // Remove parameters that no longer exist and check all params are in the right order.
2104 MyXmlNodeList todelete = new MyXmlNodeList ();
2105 foreach (XmlElement paramnode in e.SelectNodes(element)) {
2106 string name = paramnode.GetAttribute("name");
2107 if (!seenParams.ContainsKey(name)) {
2108 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2109 Error ("The following param node can only be deleted if the --delete option is given: ");
2110 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
2112 Error ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
2113 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2117 Error ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
2118 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2119 e.ParentNode.Attributes ["MemberName"].Value,
2122 Error ("\tValue={0}", paramnode.OuterXml);
2124 todelete.Add (paramnode);
2129 if ((int)seenParams[name] != idx)
2135 foreach (XmlNode n in todelete) {
2136 n.ParentNode.RemoveChild (n);
2139 // Re-insert the parameter nodes at the top of the doc section.
2141 for (int pi = values.Length-1; pi >= 0; pi--)
2142 e.PrependChild(paramnodes[pi]);
2144 // Clear all existing param nodes
2145 foreach (XmlNode paramnode in e.SelectNodes(element)) {
2146 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2147 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
2148 Console.WriteLine(paramnode.OuterXml);
2150 paramnode.ParentNode.RemoveChild(paramnode);
2156 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
2158 string existingName = pe.GetAttribute ("name");
2159 pe.SetAttribute ("name", newName);
2160 if (existingName == newName)
2162 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
2163 if (paramref.GetAttribute ("name").Trim () == existingName)
2164 paramref.SetAttribute ("name", newName);
2167 private static void NormalizeWhitespace(XmlElement e) {
2168 // Remove all text and whitespace nodes from the element so it
2169 // is outputted with nice indentation and no blank lines.
2170 ArrayList deleteNodes = new ArrayList();
2171 foreach (XmlNode n in e)
2172 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2174 foreach (XmlNode n in deleteNodes)
2175 n.ParentNode.RemoveChild(n);
2178 private static bool UpdateAssemblyVersions(XmlElement root, MemberInfo member, bool add)
2180 Type type = member as Type;
2182 type = member.DeclaringType;
2183 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion(type.Assembly) }, add);
2186 private static string GetAssemblyVersion(Assembly assembly)
2188 return assembly.GetName().Version.ToString();
2191 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
2193 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
2195 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2196 root.AppendChild(e);
2198 MyXmlNodeList matches = new MyXmlNodeList (assemblyVersions.Length);
2199 foreach (XmlElement v in e.SelectNodes ("AssemblyVersion")) {
2200 foreach (string sv in assemblyVersions)
2201 if (v.InnerText == sv)
2204 // matches.Count > 0 && add: ignore -- already present
2205 if (matches.Count > 0 && !add) {
2206 foreach (XmlNode c in matches)
2209 else if (matches.Count == 0 && add) {
2210 foreach (string sv in assemblyVersions) {
2211 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2216 // matches.Count == 0 && !add: ignore -- already not present
2218 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2219 SortXmlNodes (e, avs, new VersionComparer ());
2221 return avs.Count != 0;
2225 private static Type[] IgnorableAttributes = {
2226 // Security related attributes
2227 typeof (System.Reflection.AssemblyKeyFileAttribute),
2228 typeof (System.Reflection.AssemblyDelaySignAttribute),
2229 // Present in @RefType
2230 typeof (System.Runtime.InteropServices.OutAttribute),
2231 // For naming the indexer to use when not using indexers
2232 typeof (System.Reflection.DefaultMemberAttribute),
2233 // for decimal constants
2234 typeof (System.Runtime.CompilerServices.DecimalConstantAttribute),
2235 // compiler generated code
2236 typeof (System.Runtime.CompilerServices.CompilerGeneratedAttribute),
2237 // more compiler generated code, e.g. iterator methods
2238 typeof (System.Diagnostics.DebuggerHiddenAttribute),
2239 typeof (System.Runtime.CompilerServices.FixedBufferAttribute),
2240 typeof (System.Runtime.CompilerServices.UnsafeValueTypeAttribute),
2241 // extension methods
2242 typeof (System.Runtime.CompilerServices.ExtensionAttribute),
2246 private static void MakeAttributes(XmlElement root, object attributes, bool assemblyAttributes) {
2249 object[] at = ((ICustomAttributeProvider) attributes).GetCustomAttributes (false);
2252 System.Collections.Generic.IList<CustomAttributeData> at;
2253 if (attributes is Assembly)
2254 at = CustomAttributeData.GetCustomAttributes((Assembly)attributes);
2255 else if (attributes is MemberInfo)
2256 at = CustomAttributeData.GetCustomAttributes((MemberInfo)attributes);
2257 else if (attributes is Module)
2258 at = CustomAttributeData.GetCustomAttributes((Module)attributes);
2259 else if (attributes is ParameterInfo)
2260 at = CustomAttributeData.GetCustomAttributes((ParameterInfo)attributes);
2262 throw new ArgumentException("unsupported type: " + attributes.GetType().ToString());
2267 ClearElement(root, "Attributes");
2272 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2276 e = root.OwnerDocument.CreateElement("Attributes");
2279 foreach (CustomAttributeData a in at) {
2280 if (!IsPublic (a.Constructor.DeclaringType))
2282 if (slashdocFormatter.GetName (a.Constructor.DeclaringType) == null)
2285 if (Array.IndexOf (IgnorableAttributes, a.Constructor.DeclaringType) >= 0)
2290 StringList fields = new StringList ();
2292 foreach (CustomAttributeTypedArgument f in a.ConstructorArguments) {
2293 fields.Add(MakeAttributesValueString(f.Value));
2295 foreach (CustomAttributeNamedArgument f in a.NamedArguments) {
2296 fields.Add(f.MemberInfo.Name + "=" + MakeAttributesValueString(f.TypedValue.Value));
2299 string a2 = String.Join(", ", DocUtils.ToStringArray (fields));
2300 if (a2 != "") a2 = "(" + a2 + ")";
2302 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2305 string name = a.Constructor.DeclaringType.FullName;
2306 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
2307 WriteElementText(ae, "AttributeName", name + a2);
2310 foreach (Attribute a in at) {
2311 if (!IsPublic (a.GetType ()))
2313 if (slashdocFormatter.GetName (a.GetType ()) == null) continue; // hide non-visible attributes
2314 //if (assemblyAttributes && a.GetType().FullName.StartsWith("System.Reflection.")) continue;
2315 if (a.GetType().FullName == "System.Reflection.AssemblyKeyFileAttribute" || a.GetType().FullName == "System.Reflection.AssemblyDelaySignAttribute") continue; // hide security-related attributes
2319 // There's no way to reconstruct how the attribute's constructor was called,
2320 // so as a substitute, just list the value of all of the attribute's public fields.
2322 StringList fields = new StringList ();
2323 foreach (PropertyInfo f in a.GetType().GetProperties(BindingFlags.Public|BindingFlags.Instance)) {
2324 if (f.Name == "TypeId") continue;
2328 v = f.GetValue(a, null);
2329 if (v == null) v = "null";
2330 else if (v is string) v = "\"" + v + "\"";
2331 else if (v is Type) v = "typeof(" + GetCSharpFullName ((Type)v) + ")";
2332 else if (v is Enum) v = v.GetType().FullName + "." + v.ToString().Replace(", ", "|");
2334 catch (Exception ex) {
2335 v = "/* error getting property value: " + ex.Message + " */";
2338 fields.Add(f.Name + "=" + v);
2340 string a2 = String.Join(", ", DocUtils.ToStringArray (fields));
2341 if (a2 != "") a2 = "(" + a2 + ")";
2343 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2346 string name = a.GetType().FullName;
2347 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
2348 WriteElementText(ae, "AttributeName", name + a2);
2352 if (b && e.ParentNode == null)
2353 root.AppendChild(e);
2355 ClearElement(root, "Attributes");
2357 NormalizeWhitespace(e);
2361 private static string MakeAttributesValueString(object v) {
2362 if (v == null) return "null";
2363 else if (v is string) return "\"" + v + "\"";
2364 else if (v is bool) return (bool)v ? "true" : "false";
2365 else if (v is Type) return "typeof(" + GetCSharpFullName ((Type)v) + ")";
2366 else if (v is Enum) {
2367 string typename = v.GetType ().FullName;
2368 return typename + "." + v.ToString().Replace(", ", " | " + typename + ".");
2370 else return v.ToString();
2374 private static void MakeParameters(XmlElement root, ParameterInfo[] parameters) {
2375 XmlElement e = WriteElement(root, "Parameters");
2377 foreach (ParameterInfo p in parameters) {
2378 XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
2380 pe.SetAttribute("Name", p.Name);
2381 pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
2382 if (p.ParameterType.IsByRef) {
2383 if (p.IsOut) pe.SetAttribute("RefType", "out");
2384 else pe.SetAttribute("RefType", "ref");
2386 MakeAttributes(pe, p, false);
2390 private static void MakeTypeParameters(XmlElement root, Type[] typeParams)
2392 if (typeParams == null || typeParams.Length == 0) {
2393 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2395 root.RemoveChild (f);
2398 XmlElement e = WriteElement(root, "TypeParameters");
2400 foreach (Type t in typeParams) {
2401 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2403 pe.SetAttribute("Name", t.Name);
2404 MakeAttributes(pe, t, false);
2406 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2407 GenericParameterAttributes attrs = t.GenericParameterAttributes;
2408 Type[] constraints = t.GetGenericParameterConstraints ();
2409 if (attrs == GenericParameterAttributes.None && constraints.Length == 0) {
2417 ce = root.OwnerDocument.CreateElement ("Constraints");
2419 pe.AppendChild (ce);
2420 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2421 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2422 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2423 AppendElementText (ce, "ParameterAttribute", "Covariant");
2424 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2425 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2426 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2427 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2428 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2429 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2430 foreach (Type c in constraints) {
2431 AppendElementText (ce,
2432 c.IsInterface ? "InterfaceName" : "BaseTypeName", GetDocTypeFullName (c));
2438 private static void MakeParameters(XmlElement root, MemberInfo mi) {
2439 if (mi is ConstructorInfo) MakeParameters(root, ((ConstructorInfo)mi).GetParameters());
2440 else if (mi is MethodInfo) {
2441 MethodBase mb = (MethodBase) mi;
2442 ParameterInfo[] parameters = mb.GetParameters();
2443 MakeParameters(root, parameters);
2444 if (parameters.Length > 0 && DocUtils.IsExtensionMethod (mb)) {
2445 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2446 p.SetAttribute ("RefType", "this");
2449 else if (mi is PropertyInfo) {
2450 ParameterInfo[] parameters = ((PropertyInfo)mi).GetIndexParameters();
2451 if (parameters.Length > 0)
2452 MakeParameters(root, parameters);
2456 else if (mi is FieldInfo) return;
2457 else if (mi is EventInfo) return;
2458 else throw new ArgumentException();
2461 private static string GetDocParameterType (Type type)
2463 return GetDocTypeFullName (type).Replace ("@", "&");
2466 private static void MakeReturnValue(XmlElement root, Type type, ICustomAttributeProvider attributes) {
2467 XmlElement e = WriteElement(root, "ReturnValue");
2469 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2470 if (attributes != null)
2471 MakeAttributes(e, attributes, false);
2474 private static void MakeReturnValue(XmlElement root, MemberInfo mi) {
2475 if (mi is ConstructorInfo) return;
2476 else if (mi is MethodInfo) MakeReturnValue(root, ((MethodInfo)mi).ReturnType, ((MethodInfo)mi).ReturnTypeCustomAttributes);
2477 else if (mi is PropertyInfo) MakeReturnValue(root, ((PropertyInfo)mi).PropertyType, null);
2478 else if (mi is FieldInfo) MakeReturnValue(root, ((FieldInfo)mi).FieldType, null);
2479 else if (mi is EventInfo) MakeReturnValue(root, ((EventInfo)mi).EventHandlerType, null);
2480 else throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2483 private static XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info) {
2484 MemberInfo mi = info.Member;
2485 if (mi is Type) return null;
2487 string sigs = MakeMemberSignature(mi);
2488 if (sigs == null) return null; // not publicly visible
2490 // no documentation for property/event accessors. Is there a better way of doing this?
2491 if (mi.Name.StartsWith("get_")) return null;
2492 if (mi.Name.StartsWith("set_")) return null;
2493 if (mi.Name.StartsWith("add_")) return null;
2494 if (mi.Name.StartsWith("remove_")) return null;
2495 if (mi.Name.StartsWith("raise_")) return null;
2497 XmlElement me = doc.CreateElement("Member");
2498 me.SetAttribute("MemberName", GetMemberName (mi));
2503 if (since != null) {
2504 XmlNode docs = me.SelectSingleNode("Docs");
2505 docs.AppendChild (CreateSinceNode (doc));
2511 private static string GetMemberName (MemberInfo mi)
2513 MethodBase mb = mi as MethodBase;
2515 PropertyInfo pi = mi as PropertyInfo;
2518 return DocUtils.GetPropertyName (pi);
2520 StringBuilder sb = new StringBuilder (mi.Name.Length);
2521 if (!DocUtils.IsExplicitlyImplemented (mb))
2522 sb.Append (mi.Name);
2525 MethodInfo ifaceMethod;
2526 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2527 sb.Append (GetDocTypeFullName (iface));
2529 sb.Append (ifaceMethod.Name);
2531 if (DocUtils.GetContainsGenericParameters (mb)) {
2532 Type[] typeParams = DocUtils.GetGenericArguments (mb);
2533 if (typeParams.Length > 0) {
2535 sb.Append (typeParams [0].Name);
2536 for (int i = 1; i < typeParams.Length; ++i)
2537 sb.Append (",").Append (typeParams [i].Name);
2541 return sb.ToString ();
2544 private static int CountChars (string s, char c)
2547 for (int i = 0; i < s.Length; ++i) {
2554 static bool IsDelegate(Type type) {
2555 return typeof(System.Delegate).IsAssignableFrom (type) && !type.IsAbstract;
2558 /// SIGNATURE GENERATION FUNCTIONS
2560 private static bool InterfaceNotFromAnother(Type i, Type[] i2) {
2561 foreach (Type t in i2)
2562 if (i != t && Array.IndexOf(t.GetInterfaces(), i) != -1)
2567 static string MakeTypeSignature (Type type) {
2568 return csharpFormatter.GetDeclaration (type);
2571 static string MakeMemberSignature(MemberInfo mi) {
2572 return csharpFullFormatter.GetDeclaration (mi);
2575 static string GetMemberType(MemberInfo mi) {
2576 if (mi is ConstructorInfo) return "Constructor";
2577 if (mi is MethodInfo) return "Method";
2578 if (mi is PropertyInfo) return "Property";
2579 if (mi is FieldInfo) return "Field";
2580 if (mi is EventInfo) return "Event";
2581 throw new ArgumentException();
2584 private static string GetDocTypeName (Type type)
2586 return docTypeFormatter.GetName (type);
2589 private static string GetDocTypeFullName (Type type)
2591 return DocTypeFullMemberFormatter.Default.GetName (type);
2594 private static string GetCSharpFullName (Type type)
2596 return DocTypeFullMemberFormatter.Default.GetName (type);
2599 class DocsNodeInfo {
2600 public DocsNodeInfo (XmlElement node)
2605 public DocsNodeInfo (XmlElement node, Type type)
2611 public DocsNodeInfo (XmlElement node, MemberInfo member)
2614 SetMemberInfo (member);
2617 public void SetType (Type type)
2620 throw new ArgumentNullException ("type");
2621 GenericParameters = DocUtils.GetGenericArguments (type);
2622 if (type.DeclaringType != null) {
2623 Type[] declGenParams = DocUtils.GetGenericArguments (type.DeclaringType);
2624 if (declGenParams != null && GenericParameters.Length == declGenParams.Length) {
2625 GenericParameters = null;
2627 else if (declGenParams != null) {
2628 Type[] nestedParams = new Type [GenericParameters.Length - declGenParams.Length];
2629 for (int i = 0; i < nestedParams.Length; ++i) {
2630 nestedParams [i] = GenericParameters [i+declGenParams.Length];
2632 GenericParameters = nestedParams;
2635 if (IsDelegate(type)) {
2636 Parameters = type.GetMethod("Invoke").GetParameters();
2637 ReturnType = type.GetMethod("Invoke").ReturnType;
2639 SetSlashDocs (type);
2642 public void SetMemberInfo (MemberInfo member)
2645 throw new ArgumentNullException ("member");
2646 ReturnIsReturn = true;
2650 if (member is MethodInfo || member is ConstructorInfo) {
2651 Parameters = ((MethodBase) member).GetParameters ();
2652 if (DocUtils.GetContainsGenericParameters ((MethodBase) member)) {
2653 GenericParameters = DocUtils.GetGenericArguments ((MethodBase) member);
2656 else if (member is PropertyInfo) {
2657 Parameters = ((PropertyInfo) member).GetIndexParameters ();
2660 if (member is MethodInfo) {
2661 ReturnType = ((MethodInfo) member).ReturnType;
2662 } else if (member is PropertyInfo) {
2663 ReturnType = ((PropertyInfo) member).PropertyType;
2664 ReturnIsReturn = false;
2667 // no remarks section for enum members
2668 if (member.DeclaringType != null && member.DeclaringType.IsEnum)
2670 SetSlashDocs (member);
2673 private void SetSlashDocs (MemberInfo member)
2675 if (slashdocs == null)
2678 string slashdocsig = slashdocFormatter.GetDeclaration (member);
2679 if (slashdocsig != null)
2680 SlashDocs = slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
2683 public Type ReturnType;
2684 public Type[] GenericParameters;
2685 public ParameterInfo[] Parameters;
2686 public bool ReturnIsReturn;
2687 public XmlElement Node;
2688 public bool AddRemarks = true;
2689 public XmlNode SlashDocs;
2690 public XmlReader EcmaDocs;
2691 public MemberInfo Member;
2694 static string GetXPathForMember (DocumentationMember member)
2696 StringBuilder xpath = new StringBuilder ();
2697 xpath.Append ("//Members/Member[@MemberName=\"")
2698 .Append (member.MemberName)
2700 if (member.Parameters != null && member.Parameters.Count > 0) {
2701 xpath.Append ("/Parameters[count(Parameter) = ")
2702 .Append (member.Parameters.Count);
2703 for (int i = 0; i < member.Parameters.Count; ++i) {
2704 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2705 xpath.Append (member.Parameters [i]);
2706 xpath.Append ("\"");
2708 xpath.Append ("]/..");
2710 return xpath.ToString ();
2713 public static string GetXPathForMember (XPathNavigator member)
2715 StringBuilder xpath = new StringBuilder ();
2716 xpath.Append ("//Type[@FullName=\"")
2717 .Append (SelectSingleNode (member, "../../@FullName").Value)
2719 xpath.Append ("Members/Member[@MemberName=\"")
2720 .Append (SelectSingleNode (member, "@MemberName").Value)
2722 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2723 if (parameters.Count > 0) {
2724 xpath.Append ("/Parameters[count(Parameter) = ")
2725 .Append (parameters.Count);
2727 while (parameters.MoveNext ()) {
2729 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2730 xpath.Append (parameters.Current.Value);
2731 xpath.Append ("\"");
2733 xpath.Append ("]/..");
2735 return xpath.ToString ();
2738 public static string GetXPathForMember (MemberInfo member)
2740 StringBuilder xpath = new StringBuilder ();
2741 xpath.Append ("//Type[@FullName=\"")
2742 .Append (member.DeclaringType.FullName)
2744 xpath.Append ("Members/Member[@MemberName=\"")
2745 .Append (GetMemberName (member))
2748 ParameterInfo[] parameters = null;
2749 if (member is MethodBase)
2750 parameters = ((MethodBase) member).GetParameters ();
2751 else if (member is PropertyInfo) {
2752 parameters = ((PropertyInfo) member).GetIndexParameters ();
2754 if (parameters != null && parameters.Length > 0) {
2755 xpath.Append ("/Parameters[count(Parameter) = ")
2756 .Append (parameters.Length);
2757 for (int i = 0; i < parameters.Length; ++i) {
2758 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2759 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2760 xpath.Append ("\"");
2762 xpath.Append ("]/..");
2764 return xpath.ToString ();
2768 static class DocUtils {
2769 public static bool GetContainsGenericParameters (Type type)
2774 return type.ContainsGenericParameters;
2778 public static bool GetContainsGenericParameters (MethodBase mb)
2783 return mb.ContainsGenericParameters;
2787 public static Type[] GetGenericArguments (Type type)
2790 return new Type [0];
2792 return type.GetGenericArguments ();
2796 public static Type[] GetGenericArguments (MethodBase mb)
2799 return new Type [0];
2801 return mb.GetGenericArguments ();
2805 public static Type GetGenericTypeDefinition (Type type)
2810 return type.GetGenericTypeDefinition ();
2814 public static Type[] GetGenericParameterConstraints (Type type)
2819 return type.GetGenericParameterConstraints ();
2823 public static bool IsGenericType (Type type)
2828 return type.IsGenericType;
2832 public static bool IsGenericParameter (Type type)
2837 return type.IsGenericParameter;
2841 public static bool IsExplicitlyImplemented (MethodBase method)
2843 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2846 public static string GetTypeDotMember (string name)
2848 int startType, startMethod;
2849 startType = startMethod = -1;
2850 for (int i = 0; i < name.Length; ++i) {
2851 if (name [i] == '.') {
2852 startType = startMethod;
2856 return name.Substring (startType+1);
2859 public static string GetMember (string name)
2861 int i = name.LastIndexOf ('.');
2864 return name.Substring (i+1);
2867 public static void GetInfoForExplicitlyImplementedMethod (
2868 MethodBase method, out Type iface, out MethodInfo ifaceMethod)
2870 Type declType = method.DeclaringType;
2871 foreach (Type declIface in declType.GetInterfaces ()) {
2872 InterfaceMapping map = declType.GetInterfaceMap (declIface);
2873 for (int i = 0; i < map.TargetMethods.Length; ++i)
2874 if (method == map.TargetMethods [i]) {
2875 iface = map.InterfaceType;
2876 ifaceMethod = map.InterfaceMethods [i];
2880 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2883 public static string[] ToStringArray (StringList list)
2886 return (string[]) list.ToArray (typeof(string));
2888 return list.ToArray ();
2892 public static string GetPropertyName (PropertyInfo pi)
2894 // Issue: (g)mcs-generated assemblies that explicitly implement
2895 // properties don't specify the full namespace, just the
2896 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2897 MethodInfo method = pi.GetGetMethod (true);
2899 method = pi.GetSetMethod (true);
2900 if (!IsExplicitlyImplemented (method))
2903 // Need to determine appropriate namespace for this member.
2905 MethodInfo ifaceMethod;
2906 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2907 return string.Join (".", new string[]{
2908 DocTypeFullMemberFormatter.Default.GetName (iface),
2909 GetMember (pi.Name)});
2912 public static string PathCombine (string dir, string path)
2918 return Path.Combine (dir, path);
2921 public static bool IsExtensionMethod (MethodBase method)
2927 method.GetCustomAttributes (
2928 typeof(System.Runtime.CompilerServices.ExtensionAttribute),
2929 false).Length != 0 &&
2930 method.DeclaringType.GetCustomAttributes (
2931 typeof(System.Runtime.CompilerServices.ExtensionAttribute),
2937 class DocumentationMember {
2938 public StringToStringMap MemberSignatures = new StringToStringMap ();
2939 public string ReturnType;
2940 public StringList Parameters;
2941 public string MemberName;
2942 public string MemberType;
2944 public DocumentationMember (XmlReader reader)
2946 MemberName = reader.GetAttribute ("MemberName");
2947 int depth = reader.Depth;
2949 StringList p = new StringList ();
2951 if (reader.NodeType != XmlNodeType.Element)
2953 switch (reader.Name) {
2954 case "MemberSignature":
2955 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
2958 MemberType = reader.ReadElementString ();
2961 if (reader.Depth == depth + 2)
2962 ReturnType = reader.ReadElementString ();
2965 if (reader.Depth == depth + 2)
2966 p.Add (reader.GetAttribute ("Type"));
2969 if (reader.Depth == depth + 1)
2973 } while (go && reader.Read () && reader.Depth >= depth);
2979 public DocumentationMember (XmlNode node)
2981 MemberName = node.Attributes ["MemberName"].Value;
2982 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
2983 XmlAttribute l = n.Attributes ["Language"];
2984 XmlAttribute v = n.Attributes ["Value"];
2985 if (l != null && v != null)
2986 MemberSignatures [l.Value] = v.Value;
2988 MemberType = node.SelectSingleNode ("MemberType").InnerText;
2989 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
2991 ReturnType = rt.InnerText;
2992 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
2994 Parameters = new StringList (p.Count);
2995 for (int i = 0; i < p.Count; ++i)
2996 Parameters.Add (p [i].Attributes ["Type"].Value);
3001 public abstract class MemberFormatter {
3002 public string GetName (MemberInfo member)
3004 Type type = member as Type;
3006 return GetTypeName (type);
3007 ConstructorInfo ctor = member as ConstructorInfo;
3009 return GetConstructorName (ctor);
3010 MethodInfo method = member as MethodInfo;
3012 return GetMethodName (method);
3013 PropertyInfo prop = member as PropertyInfo;
3015 return GetPropertyName (prop);
3016 FieldInfo field = member as FieldInfo;
3018 return GetFieldName (field);
3019 EventInfo e = member as EventInfo;
3021 return GetEventName (e);
3022 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3025 protected virtual string GetTypeName (Type type)
3028 throw new ArgumentNullException ("type");
3029 return _AppendTypeName (new StringBuilder (type.Name.Length), type).ToString ();
3032 protected virtual char[] ArrayDelimeters {
3033 get {return new char[]{'[', ']'};}
3036 protected StringBuilder _AppendTypeName (StringBuilder buf, Type type)
3039 _AppendTypeName (buf, type.GetElementType ()).Append (ArrayDelimeters [0]);
3040 int rank = type.GetArrayRank ();
3042 buf.Append (new string (',', rank-1));
3043 return buf.Append (ArrayDelimeters [1]);
3046 return AppendRefTypeName (buf, type);
3048 if (type.IsPointer) {
3049 return AppendPointerTypeName (buf, type);
3051 AppendNamespace (buf, type);
3052 if (DocUtils.IsGenericParameter (type)) {
3053 return AppendTypeName (buf, type);
3055 if (!DocUtils.IsGenericType (type)) {
3056 if (type.DeclaringType != null)
3057 AppendTypeName (buf, type.DeclaringType).Append (NestedTypeSeparator);
3058 return AppendTypeName (buf, type);
3060 return AppendGenericType (buf, type);
3063 protected virtual StringBuilder AppendNamespace (StringBuilder buf, Type type)
3065 if (type.Namespace != null && type.Namespace.Length > 0)
3066 buf.Append (type.Namespace).Append ('.');
3070 protected virtual StringBuilder AppendTypeName (StringBuilder buf, Type type)
3072 return AppendTypeName (buf, type.Name);
3075 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3077 int n = typename.IndexOf ("`");
3079 return buf.Append (typename.Substring (0, n));
3080 return buf.Append (typename);
3083 protected virtual string RefTypeModifier {
3087 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, Type type)
3089 return _AppendTypeName (buf, type.GetElementType ()).Append (RefTypeModifier);
3092 protected virtual string PointerModifier {
3096 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, Type type)
3098 return _AppendTypeName (buf, type.GetElementType ()).Append (PointerModifier);
3101 protected virtual char[] GenericTypeContainer {
3102 get {return new char[]{'<', '>'};}
3105 protected virtual char NestedTypeSeparator {
3109 protected virtual StringBuilder AppendGenericType (StringBuilder buf, Type type)
3111 Type[] genArgs = DocUtils.GetGenericArguments (type);
3113 if (type.DeclaringType != null) {
3114 AppendTypeName (buf, type.DeclaringType);
3115 if (DocUtils.IsGenericType (type.DeclaringType)) {
3116 buf.Append (GenericTypeContainer [0]);
3117 int max = DocUtils.GetGenericArguments (type.DeclaringType).Length;
3118 _AppendTypeName (buf, genArgs [genArg++]);
3119 while (genArg < max) {
3121 _AppendTypeName (buf, genArgs [genArg++]);
3123 buf.Append (GenericTypeContainer [1]);
3125 buf.Append (NestedTypeSeparator);
3127 AppendTypeName (buf, type);
3128 if (genArg < genArgs.Length) {
3129 buf.Append (GenericTypeContainer [0]);
3130 _AppendTypeName (buf, genArgs [genArg++]);
3131 while (genArg < genArgs.Length) {
3133 _AppendTypeName (buf, genArgs [genArg++]);
3135 buf.Append (GenericTypeContainer [1]);
3140 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, Type type)
3145 protected virtual string GetConstructorName (ConstructorInfo constructor)
3147 return constructor.Name;
3150 protected virtual string GetMethodName (MethodInfo method)
3155 protected virtual string GetPropertyName (PropertyInfo property)
3157 return property.Name;
3160 protected virtual string GetFieldName (FieldInfo field)
3165 protected virtual string GetEventName (EventInfo e)
3170 public string GetDeclaration (MemberInfo member)
3172 Type type = member as Type;
3174 return GetTypeDeclaration (type);
3175 ConstructorInfo ctor = member as ConstructorInfo;
3177 return GetConstructorDeclaration (ctor);
3178 MethodInfo method = member as MethodInfo;
3180 return GetMethodDeclaration (method);
3181 PropertyInfo prop = member as PropertyInfo;
3183 return GetPropertyDeclaration (prop);
3184 FieldInfo field = member as FieldInfo;
3186 return GetFieldDeclaration (field);
3187 EventInfo e = member as EventInfo;
3189 return GetEventDeclaration (e);
3190 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3193 protected virtual string GetTypeDeclaration (Type type)
3196 throw new ArgumentNullException ("type");
3197 StringBuilder buf = new StringBuilder (type.Name.Length);
3198 _AppendTypeName (buf, type);
3199 AppendGenericTypeConstraints (buf, type);
3200 return buf.ToString ();
3203 protected virtual string GetConstructorDeclaration (ConstructorInfo constructor)
3205 return GetConstructorName (constructor);
3208 protected virtual string GetMethodDeclaration (MethodInfo method)
3210 // Special signature for destructors.
3211 if (method.Name == "Finalize" && method.GetParameters().Length == 0)
3212 return GetFinalizerName (method);
3214 StringBuilder buf = new StringBuilder ();
3216 AppendVisibility (buf, method);
3217 if (buf.Length == 0 &&
3218 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3221 AppendModifiers (buf, method);
3223 if (buf.Length != 0)
3225 buf.Append (GetName (method.ReturnType)).Append (" ");
3227 AppendMethodName (buf, method);
3228 AppendGenericMethod (buf, method).Append (" ");
3229 AppendParameters (buf, method, method.GetParameters ());
3230 AppendGenericMethodConstraints (buf, method);
3231 return buf.ToString ();
3234 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodBase method)
3236 return buf.Append (method.Name);
3239 protected virtual string GetFinalizerName (MethodInfo method)
3244 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodBase method)
3249 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodInfo method)
3254 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodInfo method)
3259 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodBase method, ParameterInfo[] parameters)
3264 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodInfo method)
3269 protected virtual string GetPropertyDeclaration (PropertyInfo property)
3271 return GetPropertyName (property);
3274 protected virtual string GetFieldDeclaration (FieldInfo field)
3276 return GetFieldName (field);
3279 protected virtual string GetEventDeclaration (EventInfo e)
3281 return GetEventName (e);
3285 class CSharpFullMemberFormatter : MemberFormatter {
3287 protected override StringBuilder AppendNamespace (StringBuilder buf, Type type)
3289 if (GetCSharpType (type.FullName) == null && type.Namespace != null && type.Namespace.Length > 0 && type.Namespace != "System")
3290 buf.Append (type.Namespace).Append ('.');
3294 private string GetCSharpType (string t)
3297 case "System.Byte": return "byte";
3298 case "System.SByte": return "sbyte";
3299 case "System.Int16": return "short";
3300 case "System.Int32": return "int";
3301 case "System.Int64": return "long";
3303 case "System.UInt16": return "ushort";
3304 case "System.UInt32": return "uint";
3305 case "System.UInt64": return "ulong";
3307 case "System.Single": return "float";
3308 case "System.Double": return "double";
3309 case "System.Decimal": return "decimal";
3310 case "System.Boolean": return "bool";
3311 case "System.Char": return "char";
3312 case "System.Void": return "void";
3313 case "System.String": return "string";
3314 case "System.Object": return "object";
3319 protected override StringBuilder AppendTypeName (StringBuilder buf, Type type)
3321 if (DocUtils.IsGenericParameter (type))
3322 return buf.Append (type.Name);
3323 string t = type.FullName;
3324 if (!t.StartsWith ("System.")) {
3325 return base.AppendTypeName (buf, type);
3328 string s = GetCSharpType (t);
3330 return buf.Append (s);
3332 return base.AppendTypeName (buf, type);
3335 protected override string GetTypeDeclaration (Type type)
3337 string visibility = GetTypeVisibility (type.Attributes);
3338 if (visibility == null)
3341 StringBuilder buf = new StringBuilder ();
3343 buf.Append (visibility);
3346 MemberFormatter full = new CSharpFullMemberFormatter ();
3348 if (IsDelegate(type)) {
3349 buf.Append("delegate ");
3350 MethodInfo invoke = type.GetMethod ("Invoke");
3351 buf.Append (full.GetName (invoke.ReturnType)).Append (" ");
3352 buf.Append (GetName (type));
3353 AppendParameters (buf, invoke, invoke.GetParameters ());
3354 AppendGenericTypeConstraints (buf, type);
3357 return buf.ToString();
3360 if (type.IsAbstract && !type.IsInterface)
3361 buf.Append("abstract ");
3362 if (type.IsSealed && !IsDelegate(type) && !type.IsValueType)
3363 buf.Append("sealed ");
3364 buf.Replace ("abstract sealed", "static");
3366 buf.Append (GetTypeKind (type));
3368 buf.Append (GetCSharpType (type.FullName) == null
3373 Type basetype = type.BaseType;
3374 if (basetype == typeof(object) || type.IsValueType) // don't show this in signatures
3377 ArrayList interface_names = new ArrayList ();
3378 foreach (Type i in type.GetInterfaces ())
3379 if ((type.BaseType == null || Array.IndexOf (type.BaseType.GetInterfaces (), i) == -1) &&
3380 InterfaceNotFromAnother (i, type.GetInterfaces ()))
3381 interface_names.Add (full.GetName (i));
3382 interface_names.Sort ();
3384 if (basetype != null || interface_names.Count > 0)
3387 if (basetype != null) {
3388 buf.Append (full.GetName (basetype));
3389 if (interface_names.Count > 0)
3393 for (int i = 0; i < interface_names.Count; i++){
3396 buf.Append (interface_names [i]);
3398 AppendGenericTypeConstraints (buf, type);
3401 return buf.ToString ();
3404 static string GetTypeKind (Type t)
3408 if (t.IsClass || t == typeof(System.Enum))
3414 throw new ArgumentException(t.FullName);
3417 static string GetTypeVisibility (TypeAttributes ta)
3419 switch (ta & TypeAttributes.VisibilityMask) {
3420 case TypeAttributes.Public:
3421 case TypeAttributes.NestedPublic:
3424 case TypeAttributes.NestedFamily:
3425 case TypeAttributes.NestedFamORAssem:
3433 static bool IsDelegate(Type type)
3435 return typeof (System.Delegate).IsAssignableFrom (type) && !type.IsAbstract;
3438 private static bool InterfaceNotFromAnother(Type i, Type[] i2)
3440 foreach (Type t in i2)
3441 if (i != t && Array.IndexOf (t.GetInterfaces(), i) != -1)
3446 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, Type type)
3448 if (!DocUtils.GetContainsGenericParameters (type))
3450 return AppendConstraints (buf, DocUtils.GetGenericArguments (type));
3453 private StringBuilder AppendConstraints (StringBuilder buf, Type[] genArgs)
3456 foreach (Type genArg in genArgs) {
3457 GenericParameterAttributes attrs = genArg.GenericParameterAttributes;
3458 Type[] constraints = genArg.GetGenericParameterConstraints ();
3459 if (attrs == GenericParameterAttributes.None && constraints.Length == 0)
3461 buf.Append (" where ").Append (genArg.Name).Append (" : ");
3462 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
3463 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
3464 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
3467 buf.Append ("class");
3471 buf.Append ("struct");
3474 if (constraints.Length > 0 && !isvt) {
3477 buf.Append (GetTypeName (constraints [0]));
3478 for (int i = 1; i < constraints.Length; ++i)
3479 buf.Append (", ").Append (GetTypeName (constraints [i]));
3481 if (isnew && !isvt) {
3484 buf.Append ("new()");
3491 protected override string GetConstructorDeclaration (ConstructorInfo constructor)
3493 StringBuilder buf = new StringBuilder ();
3494 AppendVisibility (buf, constructor);
3495 if (buf.Length == 0)
3499 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
3500 AppendParameters (buf, constructor, constructor.GetParameters ());
3503 return buf.ToString ();
3506 protected override string GetMethodDeclaration (MethodInfo method)
3508 string decl = base.GetMethodDeclaration (method);
3514 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodBase method)
3516 if (DocUtils.IsExplicitlyImplemented (method)) {
3518 MethodInfo ifaceMethod;
3519 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3520 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3522 .Append (ifaceMethod.Name);
3524 return base.AppendMethodName (buf, method);
3527 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodInfo method)
3529 if (!DocUtils.GetContainsGenericParameters (method))
3531 return AppendConstraints (buf, DocUtils.GetGenericArguments (method));
3534 protected override string RefTypeModifier {
3538 protected override string GetFinalizerName (MethodInfo method)
3540 return "~" + method.DeclaringType.Name + " ()";
3543 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodBase method)
3547 if (method.IsPublic)
3548 return buf.Append ("public");
3549 if (method.IsFamily || method.IsFamilyOrAssembly)
3550 return buf.Append ("protected");
3554 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodInfo method)
3556 string modifiers = String.Empty;
3557 if (method.IsStatic) modifiers += " static";
3558 if (method.IsVirtual && !method.IsAbstract) {
3559 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3560 else modifiers += " override";
3562 if (method.IsAbstract && !method.DeclaringType.IsInterface) modifiers += " abstract";
3563 if (method.IsFinal) modifiers += " sealed";
3564 if (modifiers == " virtual sealed") modifiers = "";
3566 return buf.Append (modifiers);
3569 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodInfo method)
3571 if (DocUtils.GetContainsGenericParameters (method)) {
3572 Type[] args = DocUtils.GetGenericArguments (method);
3573 if (args.Length > 0) {
3575 buf.Append (args [0].Name);
3576 for (int i = 1; i < args.Length; ++i)
3577 buf.Append (",").Append (args [i].Name);
3584 protected override StringBuilder AppendParameters (StringBuilder buf, MethodBase method, ParameterInfo[] parameters)
3586 return AppendParameters (buf, method, parameters, '(', ')');
3589 private StringBuilder AppendParameters (StringBuilder buf, MethodBase method, ParameterInfo[] parameters, char begin, char end)
3593 if (parameters.Length > 0) {
3594 if (DocUtils.IsExtensionMethod (method))
3595 buf.Append ("this ");
3596 AppendParameter (buf, parameters [0]);
3597 for (int i = 1; i < parameters.Length; ++i) {
3599 AppendParameter (buf, parameters [i]);
3603 return buf.Append (end);
3606 private StringBuilder AppendParameter (StringBuilder buf, ParameterInfo parameter)
3608 if (parameter.ParameterType.IsByRef) {
3609 if (parameter.IsOut)
3610 buf.Append ("out ");
3612 buf.Append ("ref ");
3614 buf.Append (GetName (parameter.ParameterType)).Append (" ");
3615 return buf.Append (parameter.Name);
3618 protected override string GetPropertyDeclaration (PropertyInfo property)
3622 string get_visible = null;
3623 if ((method = property.GetGetMethod (true)) != null &&
3624 (DocUtils.IsExplicitlyImplemented (method) ||
3625 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3626 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3627 string set_visible = null;
3628 if ((method = property.GetSetMethod (true)) != null &&
3629 (DocUtils.IsExplicitlyImplemented (method) ||
3630 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3631 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3633 if ((set_visible == null) && (get_visible == null))
3637 StringBuilder buf = new StringBuilder ();
3638 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
3639 buf.Append (visibility = get_visible);
3640 else if (set_visible != null && get_visible == null)
3641 buf.Append (visibility = set_visible);
3643 buf.Append (visibility = "public");
3645 // Pick an accessor to use for static/virtual/override/etc. checks.
3646 method = property.GetSetMethod (true);
3648 method = property.GetGetMethod (true);
3650 string modifiers = String.Empty;
3651 if (method.IsStatic) modifiers += " static";
3652 if (method.IsVirtual && !method.IsAbstract) {
3653 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
3654 modifiers += " virtual";
3656 modifiers += " override";
3658 if (method.IsAbstract && !method.DeclaringType.IsInterface)
3659 modifiers += " abstract";
3661 modifiers += " sealed";
3662 if (modifiers == " virtual sealed")
3664 buf.Append (modifiers).Append (' ');
3666 buf.Append (GetName (property.PropertyType)).Append (' ');
3668 MemberInfo[] defs = property.DeclaringType.GetDefaultMembers ();
3669 string name = property.Name;
3670 foreach (MemberInfo mi in defs) {
3671 if (mi == property) {
3676 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
3678 if (property.GetIndexParameters ().Length != 0) {
3679 AppendParameters (buf, method, property.GetIndexParameters (), '[', ']');
3683 if (set_visible != null) {
3684 if (set_visible != visibility)
3685 buf.Append (' ').Append (set_visible);
3686 buf.Append (" set;");
3688 if (get_visible != null) {
3689 if (get_visible != visibility)
3690 buf.Append (' ').Append (get_visible);
3691 buf.Append (" get;");
3695 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
3698 protected override string GetFieldDeclaration (FieldInfo field)
3700 if (field.DeclaringType.IsEnum && field.Name == "value__")
3701 return null; // This member of enums aren't documented.
3703 StringBuilder buf = new StringBuilder ();
3704 AppendFieldVisibility (buf, field);
3705 if (buf.Length == 0)
3708 if (field.DeclaringType.IsEnum)
3711 if (field.IsStatic && !field.IsLiteral)
3712 buf.Append (" static");
3713 if (field.IsInitOnly)
3714 buf.Append (" readonly");
3715 if (field.IsLiteral)
3716 buf.Append (" const");
3718 buf.Append (' ').Append (GetName (field.FieldType)).Append (' ');
3719 buf.Append (field.Name);
3720 AppendFieldValue (buf, field);
3723 return buf.ToString ();
3726 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldInfo field)
3729 return buf.Append ("public");
3730 if (field.IsFamily || field.IsFamilyOrAssembly)
3731 return buf.Append ("protected");
3735 static StringBuilder AppendFieldValue (StringBuilder buf, FieldInfo field)
3737 // enums have a value__ field, which we ignore, and FieldInfo.GetValue()
3738 // on a GenericType results in InvalidOperationException
3739 if (field.DeclaringType.IsEnum ||
3740 DocUtils.IsGenericType (field.DeclaringType))
3742 if (field.IsLiteral || (field.IsStatic && field.IsInitOnly)) {
3745 val = field.GetValue (null);
3750 buf.Append (" = ").Append ("null");
3751 else if (val is Enum)
3752 buf.Append (" = ").Append (val.ToString ());
3753 else if (val is IFormattable) {
3754 string value = ((IFormattable)val).ToString();
3756 value = "\"" + value + "\"";
3757 buf.Append (" = ").Append (value);
3763 protected override string GetEventDeclaration (EventInfo e)
3765 StringBuilder buf = new StringBuilder ();
3766 if (AppendVisibility (buf, e.GetAddMethod (true)).Length == 0) {
3770 AppendModifiers (buf, e.GetAddMethod (true));
3772 buf.Append (" event ");
3773 buf.Append (GetName (e.EventHandlerType)).Append (' ');
3774 buf.Append (e.Name).Append (';');
3776 return buf.ToString ();
3780 class CSharpMemberFormatter : CSharpFullMemberFormatter {
3781 protected override StringBuilder AppendNamespace (StringBuilder buf, Type type)
3787 class DocTypeFullMemberFormatter : MemberFormatter {
3788 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
3790 protected override char NestedTypeSeparator {
3795 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
3796 protected override StringBuilder AppendNamespace (StringBuilder buf, Type type)
3802 class SlashDocMemberFormatter : MemberFormatter {
3804 protected override char[] GenericTypeContainer {
3805 get {return new char[]{'{', '}'};}
3808 private bool AddTypeCount = true;
3810 protected override string GetTypeName (Type type)
3812 return base.GetTypeName (type);
3815 private Type genDeclType;
3816 private MethodBase genDeclMethod;
3818 protected override StringBuilder AppendTypeName (StringBuilder buf, Type type)
3820 if (DocUtils.IsGenericParameter (type)) {
3822 if (genDeclType != null) {
3823 Type[] genArgs = DocUtils.GetGenericArguments (genDeclType);
3824 for (int i = 0; i < genArgs.Length; ++i) {
3825 if (genArgs [i].Name == type.Name) {
3826 buf.Append ('`').Append (i);
3831 if (genDeclMethod != null) {
3832 Type[] genArgs = null;
3833 if (DocUtils.GetContainsGenericParameters (genDeclMethod)) {
3834 genArgs = DocUtils.GetGenericArguments (genDeclMethod);
3837 genArgs = new Type[0];
3838 for (int i = 0; i < genArgs.Length; ++i) {
3839 if (genArgs [i].Name == type.Name) {
3840 buf.Append ("``").Append (i);
3845 if (genDeclType == null && genDeclMethod == null) {
3846 // Probably from within an explicitly implemented interface member,
3847 // where CSC uses parameter names instead of indices (why?), e.g.
3848 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
3849 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
3850 buf.Append (type.Name);
3852 if (buf.Length == l) {
3853 throw new Exception (string.Format (
3854 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
3855 type.Name, genDeclType, genDeclMethod));
3859 base.AppendTypeName (buf, type);
3861 int numArgs = DocUtils.GetGenericArguments (type).Length;
3862 if (type.DeclaringType != null)
3863 numArgs -= DocUtils.GetGenericArguments (type).Length;
3865 buf.Append ('`').Append (numArgs);
3872 protected override StringBuilder AppendGenericType (StringBuilder buf, Type type)
3875 base.AppendGenericType (buf, type);
3877 AppendType (buf, type);
3881 private StringBuilder AppendType (StringBuilder buf, Type type)
3883 int numArgs = DocUtils.GetGenericArguments (type).Length;
3884 if (type.DeclaringType != null) {
3885 AppendType (buf, type.DeclaringType).Append (NestedTypeSeparator);
3886 numArgs -= DocUtils.GetGenericArguments (type.DeclaringType).Length;
3888 base.AppendTypeName (buf, type);
3890 buf.Append ('`').Append (numArgs);
3895 protected override string GetConstructorName (ConstructorInfo constructor)
3897 return GetMethodBaseName (constructor, "#ctor");
3900 protected override string GetMethodName (MethodInfo method)
3903 if (!DocUtils.IsExplicitlyImplemented (method))
3907 MethodInfo ifaceMethod;
3908 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3909 AddTypeCount = false;
3910 name = GetTypeName (iface) + "." + ifaceMethod.Name;
3911 AddTypeCount = true;
3913 return GetMethodBaseName (method, name);
3916 private string GetMethodBaseName (MethodBase method, string name)
3918 StringBuilder buf = new StringBuilder ();
3919 buf.Append (GetTypeName (method.DeclaringType));
3921 buf.Append (name.Replace (".", "#"));
3922 if (DocUtils.GetContainsGenericParameters (method)) {
3923 Type[] genArgs = DocUtils.GetGenericArguments (method);
3924 if (genArgs.Length > 0)
3925 buf.Append ("``").Append (genArgs.Length);
3927 ParameterInfo[] parameters = method.GetParameters ();
3928 genDeclType = method.DeclaringType;
3929 genDeclMethod = method;
3930 AppendParameters (buf, DocUtils.GetGenericArguments (method.DeclaringType), parameters);
3932 genDeclMethod = null;
3933 return buf.ToString ();
3936 private StringBuilder AppendParameters (StringBuilder buf, Type[] genArgs, ParameterInfo[] parameters)
3938 if (parameters.Length == 0)
3943 AppendParameter (buf, genArgs, parameters [0]);
3944 for (int i = 1; i < parameters.Length; ++i) {
3946 AppendParameter (buf, genArgs, parameters [i]);
3949 return buf.Append (')');
3952 private StringBuilder AppendParameter (StringBuilder buf, Type[] genArgs, ParameterInfo parameter)
3954 AddTypeCount = false;
3955 buf.Append (GetTypeName (parameter.ParameterType));
3956 AddTypeCount = true;
3960 protected override string GetPropertyName (PropertyInfo property)
3964 MethodInfo method = property.GetGetMethod (true);
3966 method = property.GetSetMethod (true);
3967 if (!DocUtils.IsExplicitlyImplemented (method))
3968 name = property.Name;
3971 MethodInfo ifaceMethod;
3972 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3973 AddTypeCount = false;
3974 name = string.Join ("#", new string[]{
3975 GetTypeName (iface).Replace (".", "#"),
3976 DocUtils.GetMember (property.Name)
3978 AddTypeCount = true;
3981 StringBuilder buf = new StringBuilder ();
3982 buf.Append (GetName (property.DeclaringType));
3985 ParameterInfo[] parameters = property.GetIndexParameters ();
3986 if (parameters.Length > 0) {
3987 genDeclType = property.DeclaringType;
3989 Type[] genArgs = DocUtils.GetGenericArguments (property.DeclaringType);
3990 AppendParameter (buf, genArgs, parameters [0]);
3991 for (int i = 1; i < parameters.Length; ++i) {
3993 AppendParameter (buf, genArgs, parameters [i]);
3998 return buf.ToString ();
4001 protected override string GetFieldName (FieldInfo field)
4003 return string.Format ("{0}.{1}",
4004 GetName (field.DeclaringType), field.Name);
4007 protected override string GetEventName (EventInfo e)
4009 return string.Format ("{0}.{1}",
4010 GetName (e.DeclaringType), e.Name);
4013 protected override string GetTypeDeclaration (Type type)
4015 string name = GetName (type);
4021 protected override string GetConstructorDeclaration (ConstructorInfo constructor)
4023 string name = GetName (constructor);
4029 protected override string GetMethodDeclaration (MethodInfo method)
4031 string name = GetName (method);
4034 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4035 genDeclType = method.DeclaringType;
4036 genDeclMethod = method;
4037 name += "~" + GetName (method.ReturnType);
4039 genDeclMethod = null;
4044 protected override string GetPropertyDeclaration (PropertyInfo property)
4046 string name = GetName (property);
4052 protected override string GetFieldDeclaration (FieldInfo field)
4054 string name = GetName (field);
4060 protected override string GetEventDeclaration (EventInfo e)
4062 string name = GetName (e);
4069 class FileNameMemberFormatter : SlashDocMemberFormatter {
4070 protected override StringBuilder AppendNamespace (StringBuilder buf, Type type)
4075 protected override char NestedTypeSeparator {