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;
28 using MemberInfoEnumerable = System.Collections.Generic.IEnumerable<System.Reflection.MemberInfo>;
29 using MyXmlNodeList = System.Collections.Generic.List<System.Xml.XmlNode>;
30 using StringList = System.Collections.Generic.List<string>;
31 using StringToStringMap = System.Collections.Generic.Dictionary<string, string>;
32 using StringToXmlNodeMap = System.Collections.Generic.Dictionary<string, System.Xml.XmlNode>;
35 namespace Mono.Documentation {
37 #pragma warning disable 0618
38 class MDocUpdaterOptions
44 [Option("The root {directory} of an assembly's documentation files.")]
46 public string path = null;
49 [Option("When updating documentation, write the updated files to this {path}.")]
51 public string updateto = null;
54 [Option(-1, "The assembly to document. Specify a {file} path or the name of a GAC'd assembly.")]
56 public string[] assembly = null;
59 [Option(-1, "Document only the {type name}d by this argument.")]
61 public string[] type = null;
64 [Option("Update only the types in this {namespace}.")]
66 public string @namespace = null;
69 [Option("Allow monodocer to delete members from files.")]
71 public bool delete = false;
74 [Option("Include overridden methods in documentation.")]
76 public bool overrides = true;
79 [Option("Don't update members.")]
81 public bool ignoremembers = false;
84 [Option("Don't rename documentation XML files for missing types. IGNORED.")]
86 public bool ignore_extra_docs = false;
89 [Option("The {name} of the project this documentation is for.")]
94 [Option("An XML documentation {file} made by the /doc option of mcs/csc the contents of which will be imported.")]
95 public string importslashdoc;
97 [Option("An ECMA or monodoc-generated XML documemntation {file} to import.")]
98 public string importecmadoc;
102 [Option("Import either a /doc or ECMA documentation file.")]
104 public string import;
107 [Option("Indent the XML files nicely.")]
109 public bool pretty = true;
112 [Option("Create a <since/> element for added types/members with the value {since}.")]
117 [Option("Show full stack trace on error.")]
119 public bool show_exceptions;
121 #pragma warning restore
129 static string srcPath;
130 static Assembly[] assemblies;
132 static bool nooverrides = true, delete = false, ignoremembers = false;
133 static bool pretty = false;
134 static bool show_exceptions = false;
136 static int additions = 0, deletions = 0;
139 static XmlDocument slashdocs;
140 static XmlReader ecmadocs;
144 static MemberFormatter csharpFullFormatter = new CSharpFullMemberFormatter ();
145 static MemberFormatter csharpFormatter = new CSharpMemberFormatter ();
146 static MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
147 static MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
148 static MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
150 static MyXmlNodeList extensionMethods = new MyXmlNodeList ();
152 const BindingFlags DefaultBindingFlags =
153 BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly;
156 public static void Main(string[] args)
158 MDocUpdaterOptions opts = new MDocUpdaterOptions ();
159 opts.ProcessArgs(args);
161 if (args.Length == 0) {
168 public override void Run (IEnumerable<string> args)
170 var opts = new MDocUpdaterOptions {
173 show_exceptions = DebugOutput,
176 var types = new List<string> ();
177 var p = new OptionSet () {
179 "Root {DIRECTORY} to generate/update documentation.",
180 v => opts.path = v },
182 "Import documentation from {FILE}.",
183 v => opts.import = v },
185 "Delete removed members from the XML files.",
186 v => opts.delete = v != null },
188 "Manually specify the assembly version that new members were added in.",
189 v => opts.since = v },
191 "Only update documentation for {TYPE}.",
192 v => types.Add (v) },
194 List<string> extra = Parse (p, args, "update",
195 "[OPTIONS]+ ASSEMBLIES",
196 "Create or update documentation from ASSEMBLIES.");
199 if (extra.Count == 0)
200 Error ("No assemblies specified.");
201 opts.assembly = extra.ToArray ();
203 opts.type = types.ToArray ();
206 opts.name = ""; // remove warning about unused member
210 public static void Run (MDocUpdaterOptions opts)
212 nooverrides = !opts.overrides;
213 delete = opts.delete;
214 ignoremembers = opts.ignoremembers;
216 pretty = opts.pretty;
218 show_exceptions = opts.show_exceptions;
221 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
223 if (opts.path == null)
224 throw new InvalidOperationException("The path option is required.");
228 if (opts.type != null && opts.type.Length > 0 && opts.@namespace != null)
229 throw new InvalidOperationException("You cannot specify both 'type' and 'namespace'.");
231 if (opts.assembly == null)
232 throw new InvalidOperationException("The assembly option is required.");
234 assemblies = new Assembly [opts.assembly.Length];
235 for (int i = 0; i < opts.assembly.Length; i++)
236 assemblies [i] = LoadAssembly (opts.assembly [i]);
241 if (opts.importslashdoc != null) {
243 slashdocs = new XmlDocument();
244 slashdocs.Load(opts.importslashdoc);
245 } catch (Exception e) {
246 Error ("Could not load /doc file: {0}", e.Message);
247 Environment.ExitCode = 1;
252 if (opts.importecmadoc != null) {
254 ecmadocs = new XmlTextReader (opts.importecmadoc);
255 } catch (Exception e) {
256 Error ("Could not load ECMA XML file: {0}", e.Message);
257 Environment.ExitCode = 1;
263 if (opts.import != null && ecmadocs == null && slashdocs == null) {
265 XmlReader r = new XmlTextReader (opts.import);
267 while (r.NodeType != XmlNodeType.Element) {
269 throw new Exception ("Unable to read XML file: " +
272 if (r.LocalName == "doc") {
273 slashdocs = new XmlDocument();
274 slashdocs.Load (opts.import);
276 else if (r.LocalName == "Libraries") {
277 ecmadocs = new XmlTextReader (opts.import);
280 throw new Exception ("Unsupported XML format within " + opts.import);
283 } catch (Exception e) {
284 Error ("Could not load XML file: {0}", e.Message);
285 Environment.ExitCode = 1;
290 // PERFORM THE UPDATES
292 string dest_dir = opts.updateto != null ? opts.updateto : opts.path;
293 if (opts.type != null && opts.type.Length > 0)
294 DoUpdateTypes(opts.path, opts.type, dest_dir);
295 else if (opts.@namespace != null)
296 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
297 Path.Combine (dest_dir, opts.@namespace));
299 DoUpdateAssemblies(opts.path, dest_dir);
301 } catch (InvalidOperationException error) {
302 Error (opts.show_exceptions ? error.ToString () : error.Message);
303 Environment.ExitCode = 1;
306 } catch (System.IO.IOException error) {
307 Error (opts.show_exceptions ? error.ToString () : error.Message);
308 Environment.ExitCode = 1;
311 } catch (Exception error) {
312 Error (opts.show_exceptions ? error.ToString () : error.Message);
313 Environment.ExitCode = 1;
316 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
323 void Error (string format, params object[] args)
325 Console.Error.Write ("monodocer: ");
326 Console.Error.WriteLine (format, args);
329 private static Assembly LoadAssembly (string name)
331 Assembly assembly = null;
333 assembly = Assembly.LoadFile (name);
334 } catch (System.IO.FileNotFoundException) { }
336 if (assembly == null) {
338 #pragma warning disable 0612
339 assembly = Assembly.LoadWithPartialName (name);
340 #pragma warning restore
341 } catch (Exception) { }
344 if (assembly == null)
345 throw new InvalidOperationException("Assembly " + name + " not found.");
350 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
351 OrderTypeAttributes (element);
352 XmlTextWriter writer = new XmlTextWriter(output);
353 writer.Formatting = Formatting.Indented;
354 writer.Indentation = 2;
355 writer.IndentChar = ' ';
356 element.WriteTo(writer);
360 private static void OrderTypeAttributes (XmlElement e)
362 foreach (XmlElement type in e.SelectNodes ("//Type")) {
363 OrderTypeAttributes (type.Attributes);
367 static readonly string[] TypeAttributeOrder = {
368 "Name", "FullName", "FullNameSP", "Maintainer"
371 private static void OrderTypeAttributes (XmlAttributeCollection c)
373 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
374 for (int i = 0; i < c.Count; ++i) {
375 XmlAttribute a = c [i];
376 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
377 if (a.Name == TypeAttributeOrder [j]) {
383 for (int i = attrs.Length-1; i >= 0; --i) {
384 XmlAttribute n = attrs [i];
387 XmlAttribute r = null;
388 for (int j = i+1; j < attrs.Length; ++j) {
389 if (attrs [j] != null) {
397 c.InsertBefore (n, r);
401 private static XmlDocument CreateIndexStub() {
402 XmlDocument index = new XmlDocument();
404 XmlElement index_root = index.CreateElement("Overview");
405 index.AppendChild(index_root);
407 if (assemblies.Length == 0)
408 throw new Exception ("No assembly");
410 XmlElement index_assemblies = index.CreateElement("Assemblies");
411 index_root.AppendChild(index_assemblies);
413 XmlElement index_remarks = index.CreateElement("Remarks");
414 index_remarks.InnerText = "To be added.";
415 index_root.AppendChild(index_remarks);
417 XmlElement index_copyright = index.CreateElement("Copyright");
418 index_copyright.InnerText = "To be added.";
419 index_root.AppendChild(index_copyright);
421 XmlElement index_types = index.CreateElement("Types");
422 index_root.AppendChild(index_types);
427 private static void WriteNamespaceStub(string ns, string outdir) {
428 XmlDocument index = new XmlDocument();
430 XmlElement index_root = index.CreateElement("Namespace");
431 index.AppendChild(index_root);
433 index_root.SetAttribute("Name", ns);
435 XmlElement index_docs = index.CreateElement("Docs");
436 index_root.AppendChild(index_docs);
438 XmlElement index_summary = index.CreateElement("summary");
439 index_summary.InnerText = "To be added.";
440 index_docs.AppendChild(index_summary);
442 XmlElement index_remarks = index.CreateElement("remarks");
443 index_remarks.InnerText = "To be added.";
444 index_docs.AppendChild(index_remarks);
446 using (TextWriter writer = OpenWrite (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew)) {
447 WriteXml(index.DocumentElement, writer);
451 public static void DoUpdateTypes(string basepath, string[] typenames, string dest) {
452 ArrayList found = new ArrayList ();
453 foreach (Assembly assembly in assemblies) {
454 foreach (DocsTypeInfo docsTypeInfo in GetTypes (assembly, typenames)) {
455 string relpath = DoUpdateType (docsTypeInfo.Type, basepath, dest, docsTypeInfo.EcmaDocs);
457 found.Add (docsTypeInfo.Type.FullName);
460 StringList notFound = new StringList (typenames.Length);
461 foreach (string typename in typenames)
462 if (!found.Contains (typename))
463 notFound.Add (typename);
464 if (notFound.Count > 0)
465 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", DocUtils.ToStringArray (notFound)));
468 public static string DoUpdateType(Type type, string basepath, string dest, XmlReader ecmaDocsType)
470 if (type.Namespace == null)
471 Error ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
473 if (!IsPublic (type))
476 // Must get the A+B form of the type name.
477 string typename = GetTypeFileName(type);
479 string reltypefile = DocUtils.PathCombine (type.Namespace, typename + ".xml");
480 string typefile = Path.Combine (basepath, reltypefile);
481 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
483 string output = null;
486 } else if (dest == "-") {
489 output = Path.Combine (dest, reltypefile);
494 XmlDocument basefile = new XmlDocument();
495 if (!pretty) basefile.PreserveWhitespace = true;
497 basefile.Load(typefile);
498 } catch (Exception e) {
499 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
502 DoUpdateType2("Updating", basefile, type, output, false, ecmaDocsType);
505 XmlElement td = StubType(type, output, ecmaDocsType);
509 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
512 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
518 private static XPathNavigator SelectSingleNode (XPathNavigator n, string xpath)
521 return n.SelectSingleNode (xpath);
523 XPathNodeIterator i = n.Select (xpath);
524 XPathNavigator r = null;
525 while (i.MoveNext ()) {
532 public static void DoUpdateNS(string ns, string nspath, string outpath) {
533 Hashtable seenTypes = new Hashtable();
534 Assembly assembly = assemblies [0];
536 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
537 XmlDocument basefile = new XmlDocument();
538 if (!pretty) basefile.PreserveWhitespace = true;
539 string typefile = Path.Combine(nspath, file.Name);
541 basefile.Load(typefile);
542 } catch (Exception e) {
543 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
547 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
548 Type type = assembly.GetType(typename, false);
550 Error ("Type no longer in assembly: " + typename);
554 seenTypes[type] = seenTypes;
555 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false, null);
558 // Stub types not in the directory
559 foreach (DocsTypeInfo docsTypeInfo in GetTypes (assembly, null)) {
560 Type type = docsTypeInfo.Type;
561 if (type.Namespace != ns || seenTypes.ContainsKey(type))
564 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"), docsTypeInfo.EcmaDocs);
565 if (td == null) continue;
569 private static string GetTypeFileName(Type type) {
570 return filenameFormatter.GetName (type);
573 public static string GetTypeFileName (string typename)
575 StringBuilder filename = new StringBuilder (typename.Length);
579 for (int i = 0; i < typename.Length; ++i) {
580 char c = typename [i];
589 filename.Append ('`').Append ((numArgs+1).ToString());
604 return filename.ToString ();
608 private static void AddIndexAssembly (Assembly assembly, XmlElement parent)
610 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
611 index_assembly.SetAttribute("Name", assembly.GetName().Name);
612 index_assembly.SetAttribute("Version", assembly.GetName().Version.ToString());
613 MakeAttributes(index_assembly, assembly, true);
614 parent.AppendChild(index_assembly);
617 private static void DoUpdateAssemblies (string source, string dest)
619 string indexfile = dest + "/index.xml";
621 if (System.IO.File.Exists(indexfile)) {
622 index = new XmlDocument();
623 index.Load(indexfile);
626 ClearElement(index.DocumentElement, "Assembly");
627 ClearElement(index.DocumentElement, "Attributes");
629 index = CreateIndexStub();
633 string defaultTitle = "Untitled";
634 if (assemblies.Length == 1)
635 defaultTitle = assemblies[0].GetName().Name;
636 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
638 WriteElementText(index.DocumentElement, "Title", name);
641 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
642 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
643 index_assemblies.RemoveAll ();
645 Hashtable goodfiles = new Hashtable();
647 foreach (Assembly assm in assemblies) {
648 AddIndexAssembly (assm, index_assemblies);
649 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
652 SortIndexEntries (index_types);
654 CleanupFiles (dest, goodfiles);
655 CleanupIndexTypes (index_types, goodfiles);
656 CleanupExtensions (index_types);
658 using (TextWriter writer = OpenWrite (indexfile, FileMode.Create))
659 WriteXml(index.DocumentElement, writer);
662 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
664 private static void DoUpdateAssembly (Assembly assembly, XmlElement index_types, string source, string dest, Hashtable goodfiles)
666 foreach (DocsTypeInfo docTypeInfo in GetTypes (assembly, null)) {
667 Type type = docTypeInfo.Type;
668 if (!IsPublic (type) || type.FullName.IndexOfAny (InvalidFilenameChars) >= 0)
671 string reltypepath = DoUpdateType (type, source, dest, docTypeInfo.EcmaDocs);
672 if (reltypepath == null)
675 // Add namespace and type nodes into the index file as needed
676 XmlElement nsnode = (XmlElement)index_types.SelectSingleNode("Namespace[@Name='" + type.Namespace + "']");
677 if (nsnode == null) {
678 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
679 nsnode.SetAttribute("Name", type.Namespace);
680 index_types.AppendChild(nsnode);
682 string typename = GetTypeFileName(type);
683 string doc_typename = GetDocTypeName (type);
684 XmlElement typenode = (XmlElement)nsnode.SelectSingleNode("Type[@Name='" + typename + "']");
685 if (typenode == null) {
686 typenode = index_types.OwnerDocument.CreateElement("Type");
687 typenode.SetAttribute("Name", typename);
688 nsnode.AppendChild(typenode);
690 if (typename != doc_typename)
691 typenode.SetAttribute("DisplayName", doc_typename);
693 typenode.RemoveAttribute("DisplayName");
694 typenode.SetAttribute ("Kind", GetTypeKind (type));
696 // Ensure the namespace index file exists
697 string onsdoc = DocUtils.PathCombine (dest, type.Namespace + ".xml");
698 string nsdoc = DocUtils.PathCombine (dest, "ns-" + type.Namespace + ".xml");
699 if (File.Exists (onsdoc)) {
700 File.Move (onsdoc, nsdoc);
703 if (!File.Exists (nsdoc)) {
704 Console.WriteLine("New Namespace File: " + type.Namespace);
705 WriteNamespaceStub(type.Namespace, dest);
708 // mark the file as corresponding to a type
709 goodfiles[reltypepath] = goodfiles;
715 public XmlReader EcmaDocs;
717 public DocsTypeInfo (Type type, XmlReader docs)
720 this.EcmaDocs = docs;
728 IEnumerable<Mono.Documentation.MDocUpdater.DocsTypeInfo>
730 GetTypes (Assembly assembly, string[] forTypes)
732 Hashtable seen = null;
733 if (forTypes != null)
734 Array.Sort (forTypes);
735 if (ecmadocs != null) {
736 seen = new Hashtable ();
738 while (ecmadocs.Read ()) {
739 switch (ecmadocs.Name) {
742 typeDepth = ecmadocs.Depth;
743 if (ecmadocs.NodeType != XmlNodeType.Element)
745 if (typeDepth != ecmadocs.Depth) // nested <Type/> element?
747 string typename = ecmadocs.GetAttribute ("FullName");
748 string typename2 = GetTypeFileName (typename);
749 if (forTypes != null &&
750 Array.BinarySearch (forTypes, typename) < 0 &&
751 typename != typename2 &&
752 Array.BinarySearch (forTypes, typename2) < 0)
755 if ((t = assembly.GetType (typename, false)) == null &&
756 (t = assembly.GetType (typename2, false)) == null)
758 seen.Add (typename, "");
759 if (typename != typename2)
760 seen.Add (typename2, "");
761 Console.WriteLine (" Import: {0}", t.FullName);
762 yield return new DocsTypeInfo (t, ecmadocs);
770 foreach (Type type in assembly.GetTypes()) {
771 if (forTypes != null && Array.BinarySearch (forTypes, type.FullName) < 0)
773 if (seen != null && seen.ContainsKey (type.FullName))
775 yield return new DocsTypeInfo (type, null);
779 private static void SortIndexEntries (XmlElement indexTypes)
781 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
782 XmlNodeComparer c = new AttributeNameComparer ();
783 SortXmlNodes (indexTypes, namespaces, c);
785 for (int i = 0; i < namespaces.Count; ++i)
786 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
789 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
791 MyXmlNodeList l = new MyXmlNodeList (children.Count);
792 for (int i = 0; i < children.Count; ++i)
793 l.Add (children [i]);
795 for (int i = l.Count - 1; i > 0; --i) {
796 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
800 abstract class XmlNodeComparer : IComparer
805 public abstract int Compare (XmlNode x, XmlNode y);
807 public int Compare (object x, object y)
809 return Compare ((XmlNode) x, (XmlNode) y);
813 class AttributeNameComparer : XmlNodeComparer {
814 public override int Compare (XmlNode x, XmlNode y)
816 return x.Attributes ["Name"].Value.CompareTo (y.Attributes ["Name"].Value);
820 class VersionComparer : XmlNodeComparer {
821 public override int Compare (XmlNode x, XmlNode y)
823 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
824 string a = GetVersion (x.InnerText);
825 string b = GetVersion (y.InnerText);
826 return new Version (a).CompareTo (new Version (b));
829 static string GetVersion (string v)
831 int n = v.IndexOf ("x");
834 return v.Substring (0, n-1);
838 private static string GetTypeKind (Type type)
841 return "Enumeration";
842 if (type.IsValueType)
844 if (type.IsInterface)
846 if (IsDelegate (type))
848 if (type.IsClass || type == typeof(System.Enum))
850 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
853 private static bool IsPublic (Type type)
856 while (decl != null) {
857 if (!(decl.IsPublic || decl.IsNestedPublic)) {
860 decl = decl.DeclaringType;
865 private static void CleanupFiles (string dest, Hashtable goodfiles)
867 // Look for files that no longer correspond to types
868 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
869 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
870 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
871 if (!goodfiles.ContainsKey(relTypeFile)) {
872 XmlDocument doc = new XmlDocument ();
873 doc.Load (typefile.FullName);
874 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
875 if (UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
876 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
877 WriteXml(doc.DocumentElement, writer);
878 goodfiles [relTypeFile] = goodfiles;
881 string newname = typefile.FullName + ".remove";
882 try { System.IO.File.Delete(newname); } catch (Exception) { }
883 try { typefile.MoveTo(newname); } catch (Exception) { }
884 Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
890 private static TextWriter OpenWrite (string path, FileMode mode)
892 return new StreamWriter (
893 new FileStream (path, mode),
894 new UTF8Encoding (false)
898 private static string[] GetAssemblyVersions ()
900 StringList versions = new StringList (assemblies.Length);
901 for (int i = 0; i < assemblies.Length; ++i)
902 versions.Add (GetAssemblyVersion (assemblies [i]));
903 return DocUtils.ToStringArray (versions);
906 private static void CleanupIndexTypes (XmlElement index_types, Hashtable goodfiles)
908 // Look for type nodes that no longer correspond to types
909 MyXmlNodeList remove = new MyXmlNodeList ();
910 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
911 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
912 if (!goodfiles.ContainsKey(fulltypename)) {
913 remove.Add (typenode);
916 foreach (XmlNode n in remove)
917 n.ParentNode.RemoveChild (n);
920 private static void CleanupExtensions (XmlElement index_types)
922 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
923 if (extensionMethods.Count == 0) {
926 index_types.RemoveChild (e);
930 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
931 index_types.SelectSingleNode ("/Overview").AppendChild (e);
935 extensionMethods.Sort (DefaultExtensionMethodComparer);
936 foreach (XmlNode m in extensionMethods) {
937 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
941 class ExtensionMethodComparer : XmlNodeComparer {
942 public override int Compare (XmlNode x, XmlNode y)
944 XmlNode xLink = x.SelectSingleNode ("Member/Link");
945 XmlNode yLink = y.SelectSingleNode ("Member/Link");
947 int n = xLink.Attributes ["Type"].Value.CompareTo (
948 yLink.Attributes ["Type"].Value);
951 n = xLink.Attributes ["Member"].Value.CompareTo (
952 yLink.Attributes ["Member"].Value);
953 if (n == 0 && !object.ReferenceEquals (x, y))
954 throw new InvalidOperationException ("Duplicate extension method found!");
959 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
961 public static void DoUpdateType2(string message, XmlDocument basefile, Type type, string output, bool insertSince, XmlReader ecmaDocsType) {
962 Console.WriteLine(message + ": " + type.FullName);
964 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
966 // Update type metadata
967 UpdateType(basefile.DocumentElement, type, ecmaDocsType);
969 if (ecmaDocsType != null) {
970 while (ecmaDocsType.Name != "Members" && ecmaDocsType.Read ()) {
973 if (ecmaDocsType.IsEmptyElement)
977 // Update existing members. Delete member nodes that no longer should be there,
978 // and remember what members are already documented so we don't add them again.
979 if (!ignoremembers) {
980 MyXmlNodeList todelete = new MyXmlNodeList ();
981 foreach (DocsNodeInfo info in GetDocumentationMembers (basefile, type, ecmaDocsType)) {
982 XmlElement oldmember = info.Node;
983 MemberInfo oldmember2 = info.Member;
984 string sig = oldmember2 != null ? MakeMemberSignature(oldmember2) : null;
986 // Interface implementations and overrides are deleted from the docs
987 // unless the overrides option is given.
988 if (oldmember2 != null && (!IsNew(oldmember2) || sig == null))
991 // Deleted (or signature changed)
992 if (oldmember2 == null) {
993 if (UpdateAssemblyVersions (oldmember, new string[]{GetAssemblyVersion (type.Assembly)}, false))
995 DeleteMember ("Member Removed", output, oldmember, todelete);
1000 if (seenmembers.ContainsKey (sig)) {
1001 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
1002 // ignore, already seen
1004 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
1005 DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
1007 Error ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
1011 // Update signature information
1014 seenmembers.Add (sig, oldmember);
1016 foreach (XmlElement oldmember in todelete)
1017 oldmember.ParentNode.RemoveChild (oldmember);
1020 if (!IsDelegate(type) && !ignoremembers) {
1021 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
1022 foreach (MemberInfo m in Sort (type.GetMembers(DefaultBindingFlags))) {
1023 if (m is Type) continue;
1025 string sig = MakeMemberSignature(m);
1026 if (sig == null) continue;
1027 if (seenmembers.ContainsKey(sig)) continue;
1029 // To be nice on diffs, members/properties/events that are overrides or are interface implementations
1030 // are not added in.
1031 if (!IsNew(m)) continue;
1033 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
1034 if (mm == null) continue;
1035 members.AppendChild( mm );
1037 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
1042 // Import code snippets from files
1043 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
1044 if (!(code is XmlElement)) continue;
1045 string file = ((XmlElement)code).GetAttribute("src");
1046 string lang = ((XmlElement)code).GetAttribute("lang");
1048 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
1050 code.InnerText = src;
1054 if (insertSince && since != null) {
1055 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
1056 docs.AppendChild (CreateSinceNode (basefile));
1060 XmlElement d = basefile.DocumentElement ["Docs"];
1061 XmlElement m = basefile.DocumentElement ["Members"];
1062 if (d != null && m != null)
1063 basefile.DocumentElement.InsertBefore (
1064 basefile.DocumentElement.RemoveChild (d), m);
1065 SortTypeMembers (m);
1068 System.IO.TextWriter writer;
1070 writer = Console.Out;
1072 FileInfo file = new FileInfo (output);
1073 if (!file.Directory.Exists) {
1074 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
1075 file.Directory.Create ();
1077 writer = OpenWrite (output, FileMode.Create);
1081 WriteXml(basefile.DocumentElement, writer);
1084 private static string GetCodeSource (string lang, string file)
1087 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
1088 // Grab the specified region
1089 string region = "#region " + file.Substring (anchorStart + 4);
1090 file = file.Substring (0, anchorStart + 3);
1092 using (StreamReader reader = new StreamReader (file)) {
1094 StringBuilder src = new StringBuilder ();
1096 while ((line = reader.ReadLine ()) != null) {
1097 if (line.Trim() == region) {
1098 indent = line.IndexOf (region);
1101 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
1106 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
1109 return src.ToString ();
1111 } catch (Exception e) {
1112 Error ("Could not load <code/> file '{0}' region '{1}': {2}",
1113 file, region, show_exceptions ? e.ToString () : e.Message);
1118 using (StreamReader reader = new StreamReader (file))
1119 return reader.ReadToEnd ();
1120 } catch (Exception e) {
1121 Error ("Could not load <code/> file '" + file + "': " + e.Message);
1130 IEnumerable<DocsNodeInfo>
1132 GetDocumentationMembers (XmlDocument basefile, Type type, XmlReader ecmaDocsMembers)
1134 if (ecmaDocsMembers != null) {
1135 int membersDepth = ecmaDocsMembers.Depth;
1137 while (go && ecmaDocsMembers.Read ()) {
1138 switch (ecmaDocsMembers.Name) {
1140 if (membersDepth != ecmaDocsMembers.Depth - 1 || ecmaDocsMembers.NodeType != XmlNodeType.Element)
1142 DocumentationMember dm = new DocumentationMember (ecmaDocsMembers);
1143 string xp = GetXPathForMember (dm);
1144 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
1146 if (oldmember == null) {
1147 m = GetMember (type, dm);
1149 Error ("Could not import ECMA docs for `{0}'s `{1}': MemberInfo not found.",
1150 type.FullName, dm.MemberSignatures ["C#"]);
1151 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
1154 // oldmember lookup may have failed due to type parameter renames.
1156 oldmember = (XmlElement) basefile.SelectSingleNode (GetXPathForMember (m));
1157 if (oldmember == null) {
1158 XmlElement members = WriteElement(basefile.DocumentElement, "Members");
1159 oldmember = basefile.CreateElement ("Member");
1160 oldmember.SetAttribute ("MemberName", dm.MemberName);
1161 members.AppendChild (oldmember);
1162 foreach (string key in Sort (dm.MemberSignatures.Keys)) {
1163 XmlElement ms = basefile.CreateElement ("MemberSignature");
1164 ms.SetAttribute ("Language", key);
1165 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
1166 oldmember.AppendChild (ms);
1168 oldmember.SetAttribute ("__monodocer-seen__", "true");
1169 Console.WriteLine ("Member Added: {0}", MakeMemberSignature (m));
1174 m = GetMember (type, new DocumentationMember (oldmember));
1176 Error ("Could not import ECMA docs for `{0}'s `{1}': MemberInfo not found.",
1177 type.FullName, dm.MemberSignatures ["C#"]);
1180 oldmember.SetAttribute ("__monodocer-seen__", "true");
1182 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
1183 if (ecmaDocsMembers.Name != "Docs")
1184 throw new InvalidOperationException ("Found " + ecmaDocsMembers.Name + "; expected <Docs/>!");
1185 node.EcmaDocs = ecmaDocsMembers;
1190 if (membersDepth == ecmaDocsMembers.Depth && ecmaDocsMembers.NodeType == XmlNodeType.EndElement) {
1197 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
1198 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
1199 oldmember.RemoveAttribute ("__monodocer-seen__");
1202 MemberInfo m = GetMember (type, new DocumentationMember (oldmember));
1204 yield return new DocsNodeInfo (oldmember);
1207 yield return new DocsNodeInfo (oldmember, m);
1212 static void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
1214 string format = output != null
1215 ? "{0}: File='{1}'; Signature='{4}'"
1216 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1220 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1221 member.Attributes ["MemberName"].Value,
1222 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
1223 if (!delete && MemberDocsHaveUserContent (member)) {
1224 Error ("Member deletions must be enabled with the --delete option.");
1226 todelete.Add (member);
1231 class MemberComparer : XmlNodeComparer {
1232 public override int Compare (XmlNode x, XmlNode y)
1235 string xMemberName = x.Attributes ["MemberName"].Value;
1236 string yMemberName = y.Attributes ["MemberName"].Value;
1238 // generic methods *end* with '>'
1239 // it's possible for explicitly implemented generic interfaces to
1240 // contain <...> without being a generic method
1241 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1242 (r = xMemberName.CompareTo (yMemberName)) != 0)
1246 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1247 xMemberName = xMemberName.Substring (0, lt);
1248 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1249 yMemberName = yMemberName.Substring (0, lt);
1250 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1253 // if @MemberName matches, then it's either two different types of
1254 // members sharing the same name, e.g. field & property, or it's an
1255 // overloaded method.
1256 // for different type, sort based on MemberType value.
1257 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1258 y.SelectSingleNode ("MemberType").InnerText);
1262 // same type -- must be an overloaded method. Sort based on type
1263 // parameter count, then parameter count, then by the parameter
1265 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1266 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1267 if (xTypeParams.Count != yTypeParams.Count)
1268 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1269 for (int i = 0; i < xTypeParams.Count; ++i) {
1270 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1271 yTypeParams [i].Attributes ["Name"].Value);
1276 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1277 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1278 if (xParams.Count != yParams.Count)
1279 return xParams.Count <= yParams.Count ? -1 : 1;
1280 for (int i = 0; i < xParams.Count; ++i) {
1281 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1282 yParams [i].Attributes ["Type"].Value);
1286 // all parameters match, but return value might not match if it was
1287 // changed between one version and another.
1288 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1289 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1290 if (xReturn != null && yReturn != null) {
1291 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1300 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1302 private static void SortTypeMembers (XmlNode members)
1304 if (members == null)
1306 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1309 private static bool MemberDocsHaveUserContent (XmlNode e)
1311 e = (XmlElement)e.SelectSingleNode("Docs");
1312 if (e == null) return false;
1313 foreach (XmlElement d in e.SelectNodes("*"))
1314 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1319 private static bool IsNew(MemberInfo m) {
1320 if (!nooverrides) return true;
1321 if (m is MethodInfo && !IsNew((MethodInfo)m)) return false;
1322 if (m is PropertyInfo && !IsNew(((PropertyInfo)m).GetGetMethod())) return false;
1323 if (m is PropertyInfo && !IsNew(((PropertyInfo)m).GetSetMethod())) return false;
1324 if (m is EventInfo && !IsNew(((EventInfo)m).GetAddMethod(true))) return false;
1325 if (m is EventInfo && !IsNew(((EventInfo)m).GetRaiseMethod())) return false;
1326 if (m is EventInfo && !IsNew(((EventInfo)m).GetRemoveMethod())) return false;
1330 private static bool IsNew(MethodInfo m) {
1331 if (m == null) return true;
1332 MethodInfo b = m.GetBaseDefinition();
1333 if (b == null || b == m) return true;
1337 // UPDATE HELPER FUNCTIONS
1340 private static XmlElement FindMatchingMember(Type type, XmlElement newfile, XmlElement oldmember) {
1341 MemberInfo oldmember2 = GetMember(type, oldmember.CreateNavigator ());
1342 if (oldmember2 == null) return null;
1344 string membername = oldmember.GetAttribute("MemberName");
1345 foreach (XmlElement newmember in newfile.SelectNodes("Members/Member[@MemberName='" + membername + "']")) {
1346 if (GetMember(type, newmember.CreateNavigator ()) == oldmember2) return newmember;
1353 private static MemberInfo GetMember(Type type, DocumentationMember member) {
1354 string membertype = member.MemberType;
1356 string returntype = member.ReturnType;
1358 string docName = member.MemberName;
1359 string[] docTypeParams = GetTypeParameters (docName);
1361 // Loop through all members in this type with the same name
1362 foreach (MemberInfo mi in GetReflectionMembers (type, docName)) {
1363 if (mi is Type) continue;
1364 if (GetMemberType(mi) != membertype) continue;
1366 string sig = MakeMemberSignature(mi);
1367 if (sig == null) continue; // not publicly visible
1369 ParameterInfo[] pis = null;
1370 string[] typeParams = null;
1371 if (mi is MethodInfo || mi is ConstructorInfo) {
1372 MethodBase mb = (MethodBase) mi;
1373 pis = mb.GetParameters();
1374 if (docTypeParams != null && DocUtils.GetContainsGenericParameters (mb)) {
1375 Type[] args = DocUtils.GetGenericArguments (mb);
1376 if (args.Length == docTypeParams.Length) {
1377 typeParams = new string [args.Length];
1378 for (int i = 0; i < args.Length; ++i)
1379 typeParams [i] = args [i].Name;
1383 else if (mi is PropertyInfo)
1384 pis = ((PropertyInfo)mi).GetIndexParameters();
1386 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
1387 int pcount = pis == null ? 0 : pis.Length;
1388 if (mcount != pcount)
1391 if (mi is MethodInfo) {
1392 // Casting operators can overload based on return type.
1393 if (returntype != GetReplacedString (
1394 GetDocTypeFullName (((MethodInfo)mi).ReturnType),
1395 typeParams, docTypeParams)) {
1403 for (int i = 0; i < pis.Length; i++) {
1404 string paramType = GetReplacedString (
1405 GetDocParameterType (pis [i].ParameterType),
1406 typeParams, docTypeParams);
1407 if (paramType != (string) member.Parameters [i]) {
1412 if (!good) continue;
1420 private static MemberInfoEnumerable GetReflectionMembers (Type type, string docName)
1422 // need to worry about 4 forms of //@MemberName values:
1423 // 1. "Normal" (non-generic) member names: GetEnumerator
1425 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
1426 // - try as-is, and try type.member (due to "kludge" for property
1428 // 3. "Normal" Generic member names: Sort<T> (CSC)
1429 // - need to remove generic parameters --> "Sort"
1430 // 4. Explicitly-implemented interface members for generic interfaces:
1431 // -- System.Collections.Generic.IEnumerable<T>.Current
1432 // - Try as-is, and try type.member, *keeping* the generic parameters.
1433 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
1434 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
1435 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
1436 // this as (1) or (2).
1437 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
1439 foreach (MemberInfo mi in type.GetMember (docName, DefaultBindingFlags))
1441 if (CountChars (docName, '.') > 0)
1442 // might be a property; try only type.member instead of
1443 // namespace.type.member.
1444 foreach (MemberInfo mi in
1445 type.GetMember (DocUtils.GetTypeDotMember (docName), DefaultBindingFlags))
1452 int startLt, startType, startMethod;
1453 startLt = startType = startMethod = -1;
1454 for (int i = 0; i < docName.Length; ++i) {
1455 switch (docName [i]) {
1464 if (numLt == 0 && (i + 1) < docName.Length)
1465 // there's another character in docName, so this <...> sequence is
1466 // probably part of a generic type -- case 4.
1470 startType = startMethod;
1476 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
1478 foreach (MemberInfo mi in type.GetMember (refName, DefaultBindingFlags))
1482 foreach (MemberInfo mi in type.GetMember (refName.Substring (startType + 1), DefaultBindingFlags))
1485 // If we _still_ haven't found it, we've hit another generic naming issue:
1486 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
1487 // explicitly-implemented METHOD names (not properties), e.g.
1488 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
1489 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
1490 // which the XML docs will contain.
1492 // Alas, we can't derive the Mono name from docName, so we need to iterate
1493 // over all member names, convert them into CSC format, and compare... :-(
1496 foreach (MemberInfo mi in type.GetMembers (DefaultBindingFlags)) {
1497 if (GetMemberName (mi) == docName)
1502 static string[] GetTypeParameters (string docName)
1504 if (docName [docName.Length-1] != '>')
1506 StringList types = new StringList ();
1507 int endToken = docName.Length-2;
1508 int i = docName.Length-2;
1510 if (docName [i] == ',' || docName [i] == '<') {
1511 types.Add (docName.Substring (i + 1, endToken - i));
1514 if (docName [i] == '<')
1519 return DocUtils.ToStringArray (types);
1522 static string GetReplacedString (string typeName, string[] from, string[] to)
1526 for (int i = 0; i < from.Length; ++i)
1527 typeName = typeName.Replace (from [i], to [i]);
1531 // CREATE A STUB DOCUMENTATION FILE
1533 public static XmlElement StubType(Type type, string output, XmlReader ecmaDocsType) {
1534 string typesig = MakeTypeSignature(type);
1535 if (typesig == null) return null; // not publicly visible
1537 XmlDocument doc = new XmlDocument();
1538 XmlElement root = doc.CreateElement("Type");
1539 doc.AppendChild (root);
1541 DoUpdateType2 ("New Type", doc, type, output, true, ecmaDocsType);
1546 private static XmlElement CreateSinceNode (XmlDocument doc)
1548 XmlElement s = doc.CreateElement ("since");
1549 s.SetAttribute ("version", since);
1553 // STUBBING/UPDATING FUNCTIONS
1555 public static void UpdateType(XmlElement root, Type type, XmlReader ecmaDocsType) {
1556 root.SetAttribute("Name", GetDocTypeName (type));
1557 root.SetAttribute("FullName", GetDocTypeFullName (type));
1559 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Language", "C#");
1560 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Value", MakeTypeSignature(type));
1562 XmlElement ass = WriteElement(root, "AssemblyInfo");
1563 WriteElementText(ass, "AssemblyName", type.Assembly.GetName().Name);
1564 UpdateAssemblyVersions(root, type, true);
1565 if (type.Assembly.GetName().CultureInfo.Name != "")
1566 WriteElementText(ass, "AssemblyCulture", type.Assembly.GetName().CultureInfo.Name);
1568 ClearElement(ass, "AssemblyCulture");
1570 // Why-oh-why do we put assembly attributes in each type file?
1571 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1572 // since they're outdated in current docs, and a waste of space.
1573 //MakeAttributes(ass, type.Assembly, true);
1574 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1575 if (assattrs != null)
1576 ass.RemoveChild(assattrs);
1578 NormalizeWhitespace(ass);
1580 if (DocUtils.IsGenericType (type)) {
1581 MakeTypeParameters (root, DocUtils.GetGenericArguments (type));
1583 ClearElement(root, "TypeParameters");
1586 if (type.BaseType != null) {
1587 XmlElement basenode = WriteElement(root, "Base");
1589 string basetypename = GetDocTypeFullName (type.BaseType);
1590 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1591 WriteElementText(root, "Base/BaseTypeName", basetypename);
1593 // Document how this type instantiates the generic parameters of its base type
1594 if (DocUtils.IsGenericType (type.BaseType)) {
1595 ClearElement(basenode, "BaseTypeArguments");
1596 Type[] baseGenArgs = DocUtils.GetGenericArguments (type.BaseType);
1597 Type genericDefinition = DocUtils.GetGenericTypeDefinition (type.BaseType);
1598 Type[] genTypeDefArgs = DocUtils.GetGenericArguments (genericDefinition);
1599 for (int i = 0; i < baseGenArgs.Length; i++) {
1600 Type typearg = baseGenArgs [i];
1601 Type typeparam = genTypeDefArgs [i];
1603 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1604 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1605 bta.AppendChild(arg);
1606 arg.SetAttribute("TypeParamName", typeparam.Name);
1607 arg.InnerText = GetDocTypeFullName (typearg);
1611 ClearElement(root, "Base");
1614 if (!IsDelegate(type) && !type.IsEnum) {
1615 // Get a sorted list of interface implementations. Don't include
1616 // interfaces that are implemented by a base type or another interface
1617 // because they go on the base type or base interface's signature.
1618 ArrayList interface_names = new ArrayList();
1619 foreach (Type i in type.GetInterfaces())
1620 if ((type.BaseType == null || Array.IndexOf(type.BaseType.GetInterfaces(), i) == -1) && InterfaceNotFromAnother(i, type.GetInterfaces()))
1621 interface_names.Add(GetDocTypeFullName (i));
1622 interface_names.Sort();
1624 XmlElement interfaces = WriteElement(root, "Interfaces");
1625 interfaces.RemoveAll();
1626 foreach (string iname in interface_names) {
1627 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1628 interfaces.AppendChild(iface);
1629 WriteElementText(iface, "InterfaceName", iname);
1632 ClearElement(root, "Interfaces");
1635 MakeAttributes(root, type, false);
1637 if (IsDelegate(type)) {
1638 MakeTypeParameters (root, DocUtils.GetGenericArguments (type));
1639 MakeParameters(root, type.GetMethod("Invoke").GetParameters());
1640 MakeReturnValue(root, type.GetMethod("Invoke"));
1643 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1644 if (ecmaDocsType != null) {
1645 if (ecmaDocsType.Name != "Docs") {
1646 int depth = ecmaDocsType.Depth;
1647 while (ecmaDocsType.Read ()) {
1648 if (ecmaDocsType.Name == "Docs" && ecmaDocsType.Depth == depth + 1)
1652 if (!ecmaDocsType.IsStartElement ("Docs"))
1653 throw new InvalidOperationException ("Found " + ecmaDocsType.Name + "; expecting <Docs/>!");
1654 typeInfo.EcmaDocs = ecmaDocsType;
1656 MakeDocNode (typeInfo);
1658 if (!IsDelegate (type))
1659 WriteElement (root, "Members");
1661 NormalizeWhitespace(root);
1664 class MemberInfoComparer : IComparer
1666 , IComparer<MemberInfo>
1669 public int Compare (MemberInfo x, MemberInfo y)
1671 string xs = slashdocFormatter.GetName (x);
1672 string ys = slashdocFormatter.GetName (y);
1673 // return String.Compare (xs, ys, StringComparison.OrdinalIgnoreCase);
1674 return string.Compare (xs, ys, true, CultureInfo.InvariantCulture);
1677 public int Compare (object x, object y)
1679 return Compare ((MemberInfo) x, (MemberInfo) y);
1683 static MemberInfoComparer memberInfoComparer = new MemberInfoComparer ();
1685 private static MemberInfo[] Sort (MemberInfo[] members)
1688 ArrayList l = new ArrayList ();
1689 l.AddRange (members);
1690 l.Sort (memberInfoComparer);
1691 return (MemberInfo[]) l.ToArray (typeof(MemberInfo));
1693 Array.Sort (members, memberInfoComparer);
1699 static IEnumerable Sort (IEnumerable list)
1701 ArrayList l = new ArrayList (list as ICollection);
1706 static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1708 List<T> l = new List<T> (list);
1714 private static void UpdateMember(DocsNodeInfo info) {
1715 XmlElement me = (XmlElement) info.Node;
1716 MemberInfo mi = info.Member;
1717 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Language", "C#");
1718 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Value", MakeMemberSignature(mi));
1720 WriteElementText(me, "MemberType", GetMemberType(mi));
1722 UpdateAssemblyVersions(me, mi, true);
1723 MakeAttributes(me, mi, false);
1724 MakeReturnValue(me, mi);
1725 if (mi is MethodBase) {
1726 MethodBase mb = (MethodBase) mi;
1727 if (DocUtils.GetContainsGenericParameters (mb))
1728 MakeTypeParameters (me, DocUtils.GetGenericArguments (mb));
1730 MakeParameters(me, mi);
1733 if (mi is FieldInfo && GetFieldConstValue((FieldInfo)mi, out fieldValue))
1734 WriteElementText(me, "MemberValue", fieldValue);
1736 info.Node = WriteElement (me, "Docs");
1738 UpdateExtensionMethods (me, info);
1741 static readonly string[] ValidExtensionMembers = {
1750 static readonly string[] ValidExtensionDocMembers = {
1756 private static void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1758 MethodInfo me = info.Member as MethodInfo;
1761 if (info.Parameters.Length < 1)
1763 if (!DocUtils.IsExtensionMethod (me))
1766 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1767 XmlNode member = e.CloneNode (true);
1768 em.AppendChild (member);
1769 RemoveExcept (member, ValidExtensionMembers);
1770 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1771 WriteElementText (member, "MemberType", "ExtensionMethod");
1772 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1773 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1774 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1775 member.AppendChild (link);
1776 AddTargets (em, info);
1778 extensionMethods.Add (em);
1781 private static void RemoveExcept (XmlNode node, string[] except)
1785 MyXmlNodeList remove = null;
1786 foreach (XmlNode n in node.ChildNodes) {
1787 if (Array.BinarySearch (except, n.Name) < 0) {
1789 remove = new MyXmlNodeList ();
1794 foreach (XmlNode n in remove)
1795 node.RemoveChild (n);
1798 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1800 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1801 member.PrependChild (targets);
1802 if (!DocUtils.IsGenericParameter (info.Parameters [0].ParameterType))
1803 AppendElementAttributeText (targets, "Target", "Type",
1804 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1806 Type[] constraints = DocUtils.GetGenericParameterConstraints (
1807 info.Parameters [0].ParameterType);
1808 if (constraints.Length == 0)
1809 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1811 foreach (Type c in constraints)
1812 AppendElementAttributeText(targets, "Target", "Type",
1813 slashdocFormatter.GetDeclaration (c));
1817 private static bool GetFieldConstValue(FieldInfo field, out string value) {
1819 if (field.DeclaringType.IsEnum) return false;
1820 if (DocUtils.IsGenericType (field.DeclaringType)) return false;
1821 if (field.IsLiteral || (field.IsStatic && field.IsInitOnly)) {
1824 val = field.GetValue(null);
1828 if (val == null) value = "null";
1829 else if (val is Enum) value = val.ToString();
1830 else if (val is IFormattable) {
1831 value = ((IFormattable)val).ToString();
1833 value = "\"" + value + "\"";
1835 if (value != null && value != "")
1841 // XML HELPER FUNCTIONS
1843 private static XmlElement WriteElement(XmlNode parent, string element) {
1844 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1846 string[] path = element.Split('/');
1847 foreach (string p in path) {
1848 ret = (XmlElement)parent.SelectSingleNode(p);
1851 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1852 ename = ename.Substring(0, ename.IndexOf('['));
1853 ret = parent.OwnerDocument.CreateElement(ename);
1854 parent.AppendChild(ret);
1863 private static void WriteElementText(XmlNode parent, string element, string value) {
1864 XmlElement node = WriteElement(parent, element);
1865 node.InnerText = value;
1869 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1871 XmlElement n = parent.OwnerDocument.CreateElement (element);
1872 parent.AppendChild (n);
1873 n.InnerText = value;
1878 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1880 XmlElement n = parent.OwnerDocument.CreateElement (element);
1881 parent.AppendChild (n);
1882 n.SetAttribute (attribute, value);
1886 private static XmlNode CopyNode (XmlNode source, XmlNode dest)
1888 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1889 dest.AppendChild (copy);
1893 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1894 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1897 node = WriteElement(parent, element);
1898 node.InnerText = value;
1900 private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
1901 XmlElement node = WriteElement(parent, element);
1902 if (node.GetAttribute(attribute) == value) return;
1903 node.SetAttribute(attribute, value);
1905 private static void ClearElement(XmlElement parent, string name) {
1906 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1908 parent.RemoveChild(node);
1911 // DOCUMENTATION HELPER FUNCTIONS
1913 private static void MakeDocNode (DocsNodeInfo info)
1915 Type[] genericParams = info.GenericParameters;
1916 ParameterInfo[] parameters = info.Parameters;
1917 Type returntype = info.ReturnType;
1918 bool returnisreturn = info.ReturnIsReturn;
1919 XmlElement e = info.Node;
1920 bool addremarks = info.AddRemarks;
1922 WriteElementInitialText(e, "summary", "To be added.");
1924 if (parameters != null) {
1925 string[] values = new string [parameters.Length];
1926 for (int i = 0; i < values.Length; ++i)
1927 values [i] = parameters [i].Name;
1928 UpdateParameters (e, "param", values);
1931 if (genericParams != null) {
1932 string[] values = new string [genericParams.Length];
1933 for (int i = 0; i < values.Length; ++i)
1934 values [i] = genericParams [i].Name;
1935 UpdateParameters (e, "typeparam", values);
1938 string retnodename = null;
1939 if (returntype != null && returntype != typeof(void)) {
1940 retnodename = returnisreturn ? "returns" : "value";
1941 string retnodename_other = !returnisreturn ? "returns" : "value";
1943 // If it has a returns node instead of a value node, change its name.
1944 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1945 if (retother != null) {
1946 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1947 foreach (XmlNode node in retother)
1948 retnode.AppendChild(node.CloneNode(true));
1949 e.ReplaceChild(retnode, retother);
1951 WriteElementInitialText(e, retnodename, "To be added.");
1954 ClearElement(e, "returns");
1955 ClearElement(e, "value");
1959 WriteElementInitialText(e, "remarks", "To be added.");
1961 if (info.EcmaDocs != null) {
1962 XmlReader r = info.EcmaDocs;
1963 int depth = r.Depth;
1964 r.ReadStartElement ("Docs");
1966 if (r.Name == "Docs") {
1967 if (r.Depth == depth && r.NodeType == XmlNodeType.EndElement)
1970 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
1972 if (!r.IsStartElement ())
1977 XmlNode doc = e.SelectSingleNode (
1978 r.Name + "[@name='" + r.GetAttribute ("name") + "']");
1979 string value = r.ReadInnerXml ();
1981 doc.InnerXml = value.Replace ("\r", "");
1988 string name = r.Name;
1989 string cref = r.GetAttribute ("cref");
1990 XmlNode doc = e.SelectSingleNode (
1991 r.Name + "[@cref='" + cref + "']");
1992 string value = r.ReadInnerXml ().Replace ("\r", "");
1994 doc.InnerXml = value;
1996 XmlElement n = e.OwnerDocument.CreateElement (name);
1997 n.SetAttribute ("cref", cref);
2004 string name = r.Name;
2005 string xpath = r.Name;
2006 StringList attributes = new StringList (r.AttributeCount);
2007 if (r.MoveToFirstAttribute ()) {
2009 attributes.Add ("@" + r.Name + "=\"" + r.Value + "\"");
2010 } while (r.MoveToNextAttribute ());
2013 if (attributes.Count > 0) {
2014 xpath += "[" + string.Join (" and ", DocUtils.ToStringArray (attributes)) + "]";
2016 XmlNode doc = e.SelectSingleNode (xpath);
2017 string value = r.ReadInnerXml ().Replace ("\r", "");
2019 doc.InnerXml = value;
2022 XmlElement n = e.OwnerDocument.CreateElement (name);
2024 foreach (string a in attributes) {
2025 int eq = a.IndexOf ('=');
2026 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
2035 if (info.SlashDocs != null) {
2036 XmlNode elem = info.SlashDocs;
2038 if (elem.SelectSingleNode("summary") != null)
2039 ClearElement(e, "summary");
2040 if (elem.SelectSingleNode("remarks") != null)
2041 ClearElement(e, "remarks");
2042 if (elem.SelectSingleNode("value") != null)
2043 ClearElement(e, "value");
2044 if (retnodename != null && elem.SelectSingleNode(retnodename) != null)
2045 ClearElement(e, retnodename);
2047 foreach (XmlNode child in elem.ChildNodes) {
2048 switch (child.Name) {
2051 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + child.Attributes ["name"].Value + "']");
2053 p2.InnerXml = child.InnerXml;
2058 case "permission": {
2059 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + child.Attributes ["cref"].Value + "']");
2061 a = e.OwnerDocument.CreateElement (child.Name);
2062 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
2065 a.InnerXml = child.InnerXml;
2069 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + child.Attributes ["cref"].Value + "']");
2071 a = e.OwnerDocument.CreateElement ("altmember");
2072 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
2078 CopyNode (child, e);
2085 OrderDocsNodes (e, e.ChildNodes);
2086 NormalizeWhitespace(e);
2089 static readonly string[] DocsNodeOrder = {
2090 "typeparam", "param", "summary", "returns", "value", "remarks",
2093 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
2095 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
2096 for (int i = 0; i < DocsNodeOrder.Length; ++i) {
2097 for (int j = 0; j < children.Count; ++j) {
2098 XmlNode c = children [j];
2099 if (c.Name == DocsNodeOrder [i]) {
2100 newChildren.Add (c);
2104 if (newChildren.Count >= 0)
2105 docs.PrependChild ((XmlNode) newChildren [0]);
2106 for (int i = 1; i < newChildren.Count; ++i) {
2107 XmlNode prev = (XmlNode) newChildren [i-1];
2108 XmlNode cur = (XmlNode) newChildren [i];
2109 docs.RemoveChild (cur);
2110 docs.InsertAfter (cur, prev);
2115 private static void UpdateParameters (XmlElement e, string element, string[] values)
2117 if (values != null) {
2118 XmlNode[] paramnodes = new XmlNode[values.Length];
2120 // Some documentation had param nodes with leading spaces.
2121 foreach (XmlElement paramnode in e.SelectNodes(element)){
2122 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
2125 // If a member has only one parameter, we can track changes to
2126 // the name of the parameter easily.
2127 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
2128 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
2131 bool reinsert = false;
2133 // Pick out existing and still-valid param nodes, and
2134 // create nodes for parameters not in the file.
2135 Hashtable seenParams = new Hashtable();
2136 for (int pi = 0; pi < values.Length; pi++) {
2137 string p = values [pi];
2140 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
2141 if (paramnodes[pi] != null) continue;
2143 XmlElement pe = e.OwnerDocument.CreateElement(element);
2144 pe.SetAttribute("name", p);
2145 pe.InnerText = "To be added.";
2146 paramnodes[pi] = pe;
2150 // Remove parameters that no longer exist and check all params are in the right order.
2152 MyXmlNodeList todelete = new MyXmlNodeList ();
2153 foreach (XmlElement paramnode in e.SelectNodes(element)) {
2154 string name = paramnode.GetAttribute("name");
2155 if (!seenParams.ContainsKey(name)) {
2156 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2157 Error ("The following param node can only be deleted if the --delete option is given: ");
2158 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
2160 Error ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
2161 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2165 Error ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
2166 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2167 e.ParentNode.Attributes ["MemberName"].Value,
2170 Error ("\tValue={0}", paramnode.OuterXml);
2172 todelete.Add (paramnode);
2177 if ((int)seenParams[name] != idx)
2183 foreach (XmlNode n in todelete) {
2184 n.ParentNode.RemoveChild (n);
2187 // Re-insert the parameter nodes at the top of the doc section.
2189 for (int pi = values.Length-1; pi >= 0; pi--)
2190 e.PrependChild(paramnodes[pi]);
2192 // Clear all existing param nodes
2193 foreach (XmlNode paramnode in e.SelectNodes(element)) {
2194 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2195 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
2196 Console.WriteLine(paramnode.OuterXml);
2198 paramnode.ParentNode.RemoveChild(paramnode);
2204 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
2206 string existingName = pe.GetAttribute ("name");
2207 pe.SetAttribute ("name", newName);
2208 if (existingName == newName)
2210 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
2211 if (paramref.GetAttribute ("name").Trim () == existingName)
2212 paramref.SetAttribute ("name", newName);
2215 private static void NormalizeWhitespace(XmlElement e) {
2216 // Remove all text and whitespace nodes from the element so it
2217 // is outputted with nice indentation and no blank lines.
2218 ArrayList deleteNodes = new ArrayList();
2219 foreach (XmlNode n in e)
2220 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2222 foreach (XmlNode n in deleteNodes)
2223 n.ParentNode.RemoveChild(n);
2226 private static bool UpdateAssemblyVersions(XmlElement root, MemberInfo member, bool add)
2228 Type type = member as Type;
2230 type = member.DeclaringType;
2231 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion(type.Assembly) }, add);
2234 private static string GetAssemblyVersion(Assembly assembly)
2236 return assembly.GetName().Version.ToString();
2239 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
2241 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
2243 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2244 root.AppendChild(e);
2246 MyXmlNodeList matches = new MyXmlNodeList (assemblyVersions.Length);
2247 foreach (XmlElement v in e.SelectNodes ("AssemblyVersion")) {
2248 foreach (string sv in assemblyVersions)
2249 if (v.InnerText == sv)
2252 // matches.Count > 0 && add: ignore -- already present
2253 if (matches.Count > 0 && !add) {
2254 foreach (XmlNode c in matches)
2257 else if (matches.Count == 0 && add) {
2258 foreach (string sv in assemblyVersions) {
2259 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2264 // matches.Count == 0 && !add: ignore -- already not present
2266 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2267 SortXmlNodes (e, avs, new VersionComparer ());
2269 return avs.Count != 0;
2273 private static Type[] IgnorableAttributes = {
2274 // Security related attributes
2275 typeof (System.Reflection.AssemblyKeyFileAttribute),
2276 typeof (System.Reflection.AssemblyDelaySignAttribute),
2277 // Present in @RefType
2278 typeof (System.Runtime.InteropServices.OutAttribute),
2279 // For naming the indexer to use when not using indexers
2280 typeof (System.Reflection.DefaultMemberAttribute),
2281 // for decimal constants
2282 typeof (System.Runtime.CompilerServices.DecimalConstantAttribute),
2283 // compiler generated code
2284 typeof (System.Runtime.CompilerServices.CompilerGeneratedAttribute),
2285 // more compiler generated code, e.g. iterator methods
2286 typeof (System.Diagnostics.DebuggerHiddenAttribute),
2287 typeof (System.Runtime.CompilerServices.FixedBufferAttribute),
2288 typeof (System.Runtime.CompilerServices.UnsafeValueTypeAttribute),
2289 // extension methods
2290 typeof (System.Runtime.CompilerServices.ExtensionAttribute),
2294 private static void MakeAttributes(XmlElement root, object attributes, bool assemblyAttributes) {
2297 object[] at = ((ICustomAttributeProvider) attributes).GetCustomAttributes (false);
2300 System.Collections.Generic.IList<CustomAttributeData> at;
2301 if (attributes is Assembly)
2302 at = CustomAttributeData.GetCustomAttributes((Assembly)attributes);
2303 else if (attributes is MemberInfo)
2304 at = CustomAttributeData.GetCustomAttributes((MemberInfo)attributes);
2305 else if (attributes is Module)
2306 at = CustomAttributeData.GetCustomAttributes((Module)attributes);
2307 else if (attributes is ParameterInfo)
2308 at = CustomAttributeData.GetCustomAttributes((ParameterInfo)attributes);
2310 throw new ArgumentException("unsupported type: " + attributes.GetType().ToString());
2315 ClearElement(root, "Attributes");
2320 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2324 e = root.OwnerDocument.CreateElement("Attributes");
2327 foreach (CustomAttributeData a in at) {
2328 if (!IsPublic (a.Constructor.DeclaringType))
2330 if (slashdocFormatter.GetName (a.Constructor.DeclaringType) == null)
2333 if (Array.IndexOf (IgnorableAttributes, a.Constructor.DeclaringType) >= 0)
2338 StringList fields = new StringList ();
2340 foreach (CustomAttributeTypedArgument f in a.ConstructorArguments) {
2341 fields.Add(MakeAttributesValueString(f.Value));
2343 foreach (CustomAttributeNamedArgument f in a.NamedArguments) {
2344 fields.Add(f.MemberInfo.Name + "=" + MakeAttributesValueString(f.TypedValue.Value));
2347 string a2 = String.Join(", ", DocUtils.ToStringArray (fields));
2348 if (a2 != "") a2 = "(" + a2 + ")";
2350 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2353 string name = a.Constructor.DeclaringType.FullName;
2354 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
2355 WriteElementText(ae, "AttributeName", name + a2);
2358 foreach (Attribute a in at) {
2359 if (!IsPublic (a.GetType ()))
2361 if (slashdocFormatter.GetName (a.GetType ()) == null) continue; // hide non-visible attributes
2362 //if (assemblyAttributes && a.GetType().FullName.StartsWith("System.Reflection.")) continue;
2363 if (a.GetType().FullName == "System.Reflection.AssemblyKeyFileAttribute" || a.GetType().FullName == "System.Reflection.AssemblyDelaySignAttribute") continue; // hide security-related attributes
2367 // There's no way to reconstruct how the attribute's constructor was called,
2368 // so as a substitute, just list the value of all of the attribute's public fields.
2370 StringList fields = new StringList ();
2371 foreach (PropertyInfo f in a.GetType().GetProperties(BindingFlags.Public|BindingFlags.Instance)) {
2372 if (f.Name == "TypeId") continue;
2376 v = f.GetValue(a, null);
2377 if (v == null) v = "null";
2378 else if (v is string) v = "\"" + v + "\"";
2379 else if (v is Type) v = "typeof(" + GetCSharpFullName ((Type)v) + ")";
2380 else if (v is Enum) v = v.GetType().FullName + "." + v.ToString().Replace(", ", "|");
2382 catch (Exception ex) {
2383 v = "/* error getting property value: " + ex.Message + " */";
2386 fields.Add(f.Name + "=" + v);
2388 string a2 = String.Join(", ", DocUtils.ToStringArray (fields));
2389 if (a2 != "") a2 = "(" + a2 + ")";
2391 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2394 string name = a.GetType().FullName;
2395 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
2396 WriteElementText(ae, "AttributeName", name + a2);
2400 if (b && e.ParentNode == null)
2401 root.AppendChild(e);
2403 ClearElement(root, "Attributes");
2405 NormalizeWhitespace(e);
2409 private static string MakeAttributesValueString(object v) {
2410 if (v == null) return "null";
2411 else if (v is string) return "\"" + v + "\"";
2412 else if (v is bool) return (bool)v ? "true" : "false";
2413 else if (v is Type) return "typeof(" + GetCSharpFullName ((Type)v) + ")";
2414 else if (v is Enum) {
2415 string typename = v.GetType ().FullName;
2416 return typename + "." + v.ToString().Replace(", ", " | " + typename + ".");
2418 else return v.ToString();
2422 private static void MakeParameters(XmlElement root, ParameterInfo[] parameters) {
2423 XmlElement e = WriteElement(root, "Parameters");
2425 foreach (ParameterInfo p in parameters) {
2426 XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
2428 pe.SetAttribute("Name", p.Name);
2429 pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
2430 if (p.ParameterType.IsByRef) {
2431 if (p.IsOut) pe.SetAttribute("RefType", "out");
2432 else pe.SetAttribute("RefType", "ref");
2434 MakeAttributes(pe, p, false);
2438 private static void MakeTypeParameters(XmlElement root, Type[] typeParams)
2440 if (typeParams == null || typeParams.Length == 0) {
2441 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2443 root.RemoveChild (f);
2446 XmlElement e = WriteElement(root, "TypeParameters");
2448 foreach (Type t in typeParams) {
2449 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2451 pe.SetAttribute("Name", t.Name);
2452 MakeAttributes(pe, t, false);
2454 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2455 GenericParameterAttributes attrs = t.GenericParameterAttributes;
2456 Type[] constraints = t.GetGenericParameterConstraints ();
2457 if (attrs == GenericParameterAttributes.None && constraints.Length == 0) {
2465 ce = root.OwnerDocument.CreateElement ("Constraints");
2467 pe.AppendChild (ce);
2468 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2469 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2470 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2471 AppendElementText (ce, "ParameterAttribute", "Covariant");
2472 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2473 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2474 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2475 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2476 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2477 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2478 foreach (Type c in constraints) {
2479 AppendElementText (ce,
2480 c.IsInterface ? "InterfaceName" : "BaseTypeName", GetDocTypeFullName (c));
2486 private static void MakeParameters(XmlElement root, MemberInfo mi) {
2487 if (mi is ConstructorInfo) MakeParameters(root, ((ConstructorInfo)mi).GetParameters());
2488 else if (mi is MethodInfo) {
2489 MethodBase mb = (MethodBase) mi;
2490 ParameterInfo[] parameters = mb.GetParameters();
2491 MakeParameters(root, parameters);
2492 if (parameters.Length > 0 && DocUtils.IsExtensionMethod (mb)) {
2493 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2494 p.SetAttribute ("RefType", "this");
2497 else if (mi is PropertyInfo) {
2498 ParameterInfo[] parameters = ((PropertyInfo)mi).GetIndexParameters();
2499 if (parameters.Length > 0)
2500 MakeParameters(root, parameters);
2504 else if (mi is FieldInfo) return;
2505 else if (mi is EventInfo) return;
2506 else throw new ArgumentException();
2509 private static string GetDocParameterType (Type type)
2511 return GetDocTypeFullName (type).Replace ("@", "&");
2514 private static void MakeReturnValue(XmlElement root, Type type, ICustomAttributeProvider attributes) {
2515 XmlElement e = WriteElement(root, "ReturnValue");
2517 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2518 if (attributes != null)
2519 MakeAttributes(e, attributes, false);
2522 private static void MakeReturnValue(XmlElement root, MemberInfo mi) {
2523 if (mi is ConstructorInfo) return;
2524 else if (mi is MethodInfo) MakeReturnValue(root, ((MethodInfo)mi).ReturnType, ((MethodInfo)mi).ReturnTypeCustomAttributes);
2525 else if (mi is PropertyInfo) MakeReturnValue(root, ((PropertyInfo)mi).PropertyType, null);
2526 else if (mi is FieldInfo) MakeReturnValue(root, ((FieldInfo)mi).FieldType, null);
2527 else if (mi is EventInfo) MakeReturnValue(root, ((EventInfo)mi).EventHandlerType, null);
2528 else throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2531 private static XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info) {
2532 MemberInfo mi = info.Member;
2533 if (mi is Type) return null;
2535 string sigs = MakeMemberSignature(mi);
2536 if (sigs == null) return null; // not publicly visible
2538 // no documentation for property/event accessors. Is there a better way of doing this?
2539 if (mi.Name.StartsWith("get_")) return null;
2540 if (mi.Name.StartsWith("set_")) return null;
2541 if (mi.Name.StartsWith("add_")) return null;
2542 if (mi.Name.StartsWith("remove_")) return null;
2543 if (mi.Name.StartsWith("raise_")) return null;
2545 XmlElement me = doc.CreateElement("Member");
2546 me.SetAttribute("MemberName", GetMemberName (mi));
2551 if (since != null) {
2552 XmlNode docs = me.SelectSingleNode("Docs");
2553 docs.AppendChild (CreateSinceNode (doc));
2559 private static string GetMemberName (MemberInfo mi)
2561 MethodBase mb = mi as MethodBase;
2563 PropertyInfo pi = mi as PropertyInfo;
2566 return DocUtils.GetPropertyName (pi);
2568 StringBuilder sb = new StringBuilder (mi.Name.Length);
2569 if (!DocUtils.IsExplicitlyImplemented (mb))
2570 sb.Append (mi.Name);
2573 MethodInfo ifaceMethod;
2574 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2575 sb.Append (GetDocTypeFullName (iface));
2577 sb.Append (ifaceMethod.Name);
2579 if (DocUtils.GetContainsGenericParameters (mb)) {
2580 Type[] typeParams = DocUtils.GetGenericArguments (mb);
2581 if (typeParams.Length > 0) {
2583 sb.Append (typeParams [0].Name);
2584 for (int i = 1; i < typeParams.Length; ++i)
2585 sb.Append (",").Append (typeParams [i].Name);
2589 return sb.ToString ();
2592 private static int CountChars (string s, char c)
2595 for (int i = 0; i < s.Length; ++i) {
2602 static bool IsDelegate(Type type) {
2603 return typeof(System.Delegate).IsAssignableFrom (type) && !type.IsAbstract;
2606 /// SIGNATURE GENERATION FUNCTIONS
2608 private static bool InterfaceNotFromAnother(Type i, Type[] i2) {
2609 foreach (Type t in i2)
2610 if (i != t && Array.IndexOf(t.GetInterfaces(), i) != -1)
2615 static string MakeTypeSignature (Type type) {
2616 return csharpFormatter.GetDeclaration (type);
2619 static string MakeMemberSignature(MemberInfo mi) {
2620 return csharpFullFormatter.GetDeclaration (mi);
2623 static string GetMemberType(MemberInfo mi) {
2624 if (mi is ConstructorInfo) return "Constructor";
2625 if (mi is MethodInfo) return "Method";
2626 if (mi is PropertyInfo) return "Property";
2627 if (mi is FieldInfo) return "Field";
2628 if (mi is EventInfo) return "Event";
2629 throw new ArgumentException();
2632 private static string GetDocTypeName (Type type)
2634 return docTypeFormatter.GetName (type);
2637 private static string GetDocTypeFullName (Type type)
2639 return DocTypeFullMemberFormatter.Default.GetName (type);
2642 private static string GetCSharpFullName (Type type)
2644 return DocTypeFullMemberFormatter.Default.GetName (type);
2647 class DocsNodeInfo {
2648 public DocsNodeInfo (XmlElement node)
2653 public DocsNodeInfo (XmlElement node, Type type)
2659 public DocsNodeInfo (XmlElement node, MemberInfo member)
2662 SetMemberInfo (member);
2665 public void SetType (Type type)
2668 throw new ArgumentNullException ("type");
2669 GenericParameters = DocUtils.GetGenericArguments (type);
2670 if (type.DeclaringType != null) {
2671 Type[] declGenParams = DocUtils.GetGenericArguments (type.DeclaringType);
2672 if (declGenParams != null && GenericParameters.Length == declGenParams.Length) {
2673 GenericParameters = null;
2675 else if (declGenParams != null) {
2676 Type[] nestedParams = new Type [GenericParameters.Length - declGenParams.Length];
2677 for (int i = 0; i < nestedParams.Length; ++i) {
2678 nestedParams [i] = GenericParameters [i+declGenParams.Length];
2680 GenericParameters = nestedParams;
2683 if (IsDelegate(type)) {
2684 Parameters = type.GetMethod("Invoke").GetParameters();
2685 ReturnType = type.GetMethod("Invoke").ReturnType;
2687 SetSlashDocs (type);
2690 public void SetMemberInfo (MemberInfo member)
2693 throw new ArgumentNullException ("member");
2694 ReturnIsReturn = true;
2698 if (member is MethodInfo || member is ConstructorInfo) {
2699 Parameters = ((MethodBase) member).GetParameters ();
2700 if (DocUtils.GetContainsGenericParameters ((MethodBase) member)) {
2701 GenericParameters = DocUtils.GetGenericArguments ((MethodBase) member);
2704 else if (member is PropertyInfo) {
2705 Parameters = ((PropertyInfo) member).GetIndexParameters ();
2708 if (member is MethodInfo) {
2709 ReturnType = ((MethodInfo) member).ReturnType;
2710 } else if (member is PropertyInfo) {
2711 ReturnType = ((PropertyInfo) member).PropertyType;
2712 ReturnIsReturn = false;
2715 // no remarks section for enum members
2716 if (member.DeclaringType != null && member.DeclaringType.IsEnum)
2718 SetSlashDocs (member);
2721 private void SetSlashDocs (MemberInfo member)
2723 if (slashdocs == null)
2726 string slashdocsig = slashdocFormatter.GetDeclaration (member);
2727 if (slashdocsig != null)
2728 SlashDocs = slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
2731 public Type ReturnType;
2732 public Type[] GenericParameters;
2733 public ParameterInfo[] Parameters;
2734 public bool ReturnIsReturn;
2735 public XmlElement Node;
2736 public bool AddRemarks = true;
2737 public XmlNode SlashDocs;
2738 public XmlReader EcmaDocs;
2739 public MemberInfo Member;
2742 static string GetXPathForMember (DocumentationMember member)
2744 StringBuilder xpath = new StringBuilder ();
2745 xpath.Append ("//Members/Member[@MemberName=\"")
2746 .Append (member.MemberName)
2748 if (member.Parameters != null && member.Parameters.Count > 0) {
2749 xpath.Append ("/Parameters[count(Parameter) = ")
2750 .Append (member.Parameters.Count);
2751 for (int i = 0; i < member.Parameters.Count; ++i) {
2752 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2753 xpath.Append (member.Parameters [i]);
2754 xpath.Append ("\"");
2756 xpath.Append ("]/..");
2758 return xpath.ToString ();
2761 public static string GetXPathForMember (XPathNavigator member)
2763 StringBuilder xpath = new StringBuilder ();
2764 xpath.Append ("//Type[@FullName=\"")
2765 .Append (SelectSingleNode (member, "../../@FullName").Value)
2767 xpath.Append ("Members/Member[@MemberName=\"")
2768 .Append (SelectSingleNode (member, "@MemberName").Value)
2770 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2771 if (parameters.Count > 0) {
2772 xpath.Append ("/Parameters[count(Parameter) = ")
2773 .Append (parameters.Count);
2775 while (parameters.MoveNext ()) {
2777 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2778 xpath.Append (parameters.Current.Value);
2779 xpath.Append ("\"");
2781 xpath.Append ("]/..");
2783 return xpath.ToString ();
2786 public static string GetXPathForMember (MemberInfo member)
2788 StringBuilder xpath = new StringBuilder ();
2789 xpath.Append ("//Type[@FullName=\"")
2790 .Append (member.DeclaringType.FullName)
2792 xpath.Append ("Members/Member[@MemberName=\"")
2793 .Append (GetMemberName (member))
2796 ParameterInfo[] parameters = null;
2797 if (member is MethodBase)
2798 parameters = ((MethodBase) member).GetParameters ();
2799 else if (member is PropertyInfo) {
2800 parameters = ((PropertyInfo) member).GetIndexParameters ();
2802 if (parameters != null && parameters.Length > 0) {
2803 xpath.Append ("/Parameters[count(Parameter) = ")
2804 .Append (parameters.Length);
2805 for (int i = 0; i < parameters.Length; ++i) {
2806 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2807 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2808 xpath.Append ("\"");
2810 xpath.Append ("]/..");
2812 return xpath.ToString ();
2816 static class DocUtils {
2817 public static bool GetContainsGenericParameters (Type type)
2822 return type.ContainsGenericParameters;
2826 public static bool GetContainsGenericParameters (MethodBase mb)
2831 return mb.ContainsGenericParameters;
2835 public static Type[] GetGenericArguments (Type type)
2838 return new Type [0];
2840 return type.GetGenericArguments ();
2844 public static Type[] GetGenericArguments (MethodBase mb)
2847 return new Type [0];
2849 return mb.GetGenericArguments ();
2853 public static Type GetGenericTypeDefinition (Type type)
2858 return type.GetGenericTypeDefinition ();
2862 public static Type[] GetGenericParameterConstraints (Type type)
2867 return type.GetGenericParameterConstraints ();
2871 public static bool IsGenericType (Type type)
2876 return type.IsGenericType;
2880 public static bool IsGenericParameter (Type type)
2885 return type.IsGenericParameter;
2889 public static bool IsExplicitlyImplemented (MethodBase method)
2891 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2894 public static string GetTypeDotMember (string name)
2896 int startType, startMethod;
2897 startType = startMethod = -1;
2898 for (int i = 0; i < name.Length; ++i) {
2899 if (name [i] == '.') {
2900 startType = startMethod;
2904 return name.Substring (startType+1);
2907 public static string GetMember (string name)
2909 int i = name.LastIndexOf ('.');
2912 return name.Substring (i+1);
2915 public static void GetInfoForExplicitlyImplementedMethod (
2916 MethodBase method, out Type iface, out MethodInfo ifaceMethod)
2918 Type declType = method.DeclaringType;
2919 foreach (Type declIface in declType.GetInterfaces ()) {
2920 InterfaceMapping map = declType.GetInterfaceMap (declIface);
2921 for (int i = 0; i < map.TargetMethods.Length; ++i)
2922 if (method == map.TargetMethods [i]) {
2923 iface = map.InterfaceType;
2924 ifaceMethod = map.InterfaceMethods [i];
2928 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2931 public static string[] ToStringArray (StringList list)
2934 return (string[]) list.ToArray (typeof(string));
2936 return list.ToArray ();
2940 public static string GetPropertyName (PropertyInfo pi)
2942 // Issue: (g)mcs-generated assemblies that explicitly implement
2943 // properties don't specify the full namespace, just the
2944 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2945 MethodInfo method = pi.GetGetMethod (true);
2947 method = pi.GetSetMethod (true);
2948 if (!IsExplicitlyImplemented (method))
2951 // Need to determine appropriate namespace for this member.
2953 MethodInfo ifaceMethod;
2954 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2955 return string.Join (".", new string[]{
2956 DocTypeFullMemberFormatter.Default.GetName (iface),
2957 GetMember (pi.Name)});
2960 public static string PathCombine (string dir, string path)
2966 return Path.Combine (dir, path);
2969 public static bool IsExtensionMethod (MethodBase method)
2975 method.GetCustomAttributes (
2976 typeof(System.Runtime.CompilerServices.ExtensionAttribute),
2977 false).Length != 0 &&
2978 method.DeclaringType.GetCustomAttributes (
2979 typeof(System.Runtime.CompilerServices.ExtensionAttribute),
2985 class DocumentationMember {
2986 public StringToStringMap MemberSignatures = new StringToStringMap ();
2987 public string ReturnType;
2988 public StringList Parameters;
2989 public string MemberName;
2990 public string MemberType;
2992 public DocumentationMember (XmlReader reader)
2994 MemberName = reader.GetAttribute ("MemberName");
2995 int depth = reader.Depth;
2997 StringList p = new StringList ();
2999 if (reader.NodeType != XmlNodeType.Element)
3001 switch (reader.Name) {
3002 case "MemberSignature":
3003 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3006 MemberType = reader.ReadElementString ();
3009 if (reader.Depth == depth + 2)
3010 ReturnType = reader.ReadElementString ();
3013 if (reader.Depth == depth + 2)
3014 p.Add (reader.GetAttribute ("Type"));
3017 if (reader.Depth == depth + 1)
3021 } while (go && reader.Read () && reader.Depth >= depth);
3027 public DocumentationMember (XmlNode node)
3029 MemberName = node.Attributes ["MemberName"].Value;
3030 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3031 XmlAttribute l = n.Attributes ["Language"];
3032 XmlAttribute v = n.Attributes ["Value"];
3033 if (l != null && v != null)
3034 MemberSignatures [l.Value] = v.Value;
3036 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3037 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
3039 ReturnType = rt.InnerText;
3040 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
3042 Parameters = new StringList (p.Count);
3043 for (int i = 0; i < p.Count; ++i)
3044 Parameters.Add (p [i].Attributes ["Type"].Value);
3049 public abstract class MemberFormatter {
3050 public string GetName (MemberInfo member)
3052 Type type = member as Type;
3054 return GetTypeName (type);
3055 ConstructorInfo ctor = member as ConstructorInfo;
3057 return GetConstructorName (ctor);
3058 MethodInfo method = member as MethodInfo;
3060 return GetMethodName (method);
3061 PropertyInfo prop = member as PropertyInfo;
3063 return GetPropertyName (prop);
3064 FieldInfo field = member as FieldInfo;
3066 return GetFieldName (field);
3067 EventInfo e = member as EventInfo;
3069 return GetEventName (e);
3070 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3073 protected virtual string GetTypeName (Type type)
3076 throw new ArgumentNullException ("type");
3077 return _AppendTypeName (new StringBuilder (type.Name.Length), type).ToString ();
3080 protected virtual char[] ArrayDelimeters {
3081 get {return new char[]{'[', ']'};}
3084 protected StringBuilder _AppendTypeName (StringBuilder buf, Type type)
3087 _AppendTypeName (buf, type.GetElementType ()).Append (ArrayDelimeters [0]);
3088 int rank = type.GetArrayRank ();
3090 buf.Append (new string (',', rank-1));
3091 return buf.Append (ArrayDelimeters [1]);
3094 return AppendRefTypeName (buf, type);
3096 if (type.IsPointer) {
3097 return AppendPointerTypeName (buf, type);
3099 AppendNamespace (buf, type);
3100 if (DocUtils.IsGenericParameter (type)) {
3101 return AppendTypeName (buf, type);
3103 if (!DocUtils.IsGenericType (type)) {
3104 return AppendFullTypeName (buf, type);
3106 return AppendGenericType (buf, type);
3109 protected virtual StringBuilder AppendNamespace (StringBuilder buf, Type type)
3111 if (type.Namespace != null && type.Namespace.Length > 0)
3112 buf.Append (type.Namespace).Append ('.');
3116 private StringBuilder AppendFullTypeName (StringBuilder buf, Type type)
3118 if (type.DeclaringType != null)
3119 AppendFullTypeName (buf, type.DeclaringType).Append (NestedTypeSeparator);
3120 return AppendTypeName (buf, type);
3123 protected virtual StringBuilder AppendTypeName (StringBuilder buf, Type type)
3125 return AppendTypeName (buf, type.Name);
3128 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3130 int n = typename.IndexOf ("`");
3132 return buf.Append (typename.Substring (0, n));
3133 return buf.Append (typename);
3136 protected virtual string RefTypeModifier {
3140 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, Type type)
3142 return _AppendTypeName (buf, type.GetElementType ()).Append (RefTypeModifier);
3145 protected virtual string PointerModifier {
3149 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, Type type)
3151 return _AppendTypeName (buf, type.GetElementType ()).Append (PointerModifier);
3154 protected virtual char[] GenericTypeContainer {
3155 get {return new char[]{'<', '>'};}
3158 protected virtual char NestedTypeSeparator {
3162 protected virtual StringBuilder AppendGenericType (StringBuilder buf, Type type)
3164 Type[] genArgs = DocUtils.GetGenericArguments (type);
3166 if (type.DeclaringType != null) {
3167 AppendTypeName (buf, type.DeclaringType);
3168 if (DocUtils.IsGenericType (type.DeclaringType)) {
3169 buf.Append (GenericTypeContainer [0]);
3170 int max = DocUtils.GetGenericArguments (type.DeclaringType).Length;
3171 _AppendTypeName (buf, genArgs [genArg++]);
3172 while (genArg < max) {
3174 _AppendTypeName (buf, genArgs [genArg++]);
3176 buf.Append (GenericTypeContainer [1]);
3178 buf.Append (NestedTypeSeparator);
3180 AppendTypeName (buf, type);
3181 if (genArg < genArgs.Length) {
3182 buf.Append (GenericTypeContainer [0]);
3183 _AppendTypeName (buf, genArgs [genArg++]);
3184 while (genArg < genArgs.Length) {
3186 _AppendTypeName (buf, genArgs [genArg++]);
3188 buf.Append (GenericTypeContainer [1]);
3193 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, Type type)
3198 protected virtual string GetConstructorName (ConstructorInfo constructor)
3200 return constructor.Name;
3203 protected virtual string GetMethodName (MethodInfo method)
3208 protected virtual string GetPropertyName (PropertyInfo property)
3210 return property.Name;
3213 protected virtual string GetFieldName (FieldInfo field)
3218 protected virtual string GetEventName (EventInfo e)
3223 public string GetDeclaration (MemberInfo member)
3225 Type type = member as Type;
3227 return GetTypeDeclaration (type);
3228 ConstructorInfo ctor = member as ConstructorInfo;
3230 return GetConstructorDeclaration (ctor);
3231 MethodInfo method = member as MethodInfo;
3233 return GetMethodDeclaration (method);
3234 PropertyInfo prop = member as PropertyInfo;
3236 return GetPropertyDeclaration (prop);
3237 FieldInfo field = member as FieldInfo;
3239 return GetFieldDeclaration (field);
3240 EventInfo e = member as EventInfo;
3242 return GetEventDeclaration (e);
3243 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3246 protected virtual string GetTypeDeclaration (Type type)
3249 throw new ArgumentNullException ("type");
3250 StringBuilder buf = new StringBuilder (type.Name.Length);
3251 _AppendTypeName (buf, type);
3252 AppendGenericTypeConstraints (buf, type);
3253 return buf.ToString ();
3256 protected virtual string GetConstructorDeclaration (ConstructorInfo constructor)
3258 return GetConstructorName (constructor);
3261 protected virtual string GetMethodDeclaration (MethodInfo method)
3263 // Special signature for destructors.
3264 if (method.Name == "Finalize" && method.GetParameters().Length == 0)
3265 return GetFinalizerName (method);
3267 StringBuilder buf = new StringBuilder ();
3269 AppendVisibility (buf, method);
3270 if (buf.Length == 0 &&
3271 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3274 AppendModifiers (buf, method);
3276 if (buf.Length != 0)
3278 buf.Append (GetName (method.ReturnType)).Append (" ");
3280 AppendMethodName (buf, method);
3281 AppendGenericMethod (buf, method).Append (" ");
3282 AppendParameters (buf, method, method.GetParameters ());
3283 AppendGenericMethodConstraints (buf, method);
3284 return buf.ToString ();
3287 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodBase method)
3289 return buf.Append (method.Name);
3292 protected virtual string GetFinalizerName (MethodInfo method)
3297 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodBase method)
3302 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodInfo method)
3307 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodInfo method)
3312 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodBase method, ParameterInfo[] parameters)
3317 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodInfo method)
3322 protected virtual string GetPropertyDeclaration (PropertyInfo property)
3324 return GetPropertyName (property);
3327 protected virtual string GetFieldDeclaration (FieldInfo field)
3329 return GetFieldName (field);
3332 protected virtual string GetEventDeclaration (EventInfo e)
3334 return GetEventName (e);
3338 class CSharpFullMemberFormatter : MemberFormatter {
3340 protected override StringBuilder AppendNamespace (StringBuilder buf, Type type)
3342 if (GetCSharpType (type.FullName) == null && type.Namespace != null && type.Namespace.Length > 0 && type.Namespace != "System")
3343 buf.Append (type.Namespace).Append ('.');
3347 private string GetCSharpType (string t)
3350 case "System.Byte": return "byte";
3351 case "System.SByte": return "sbyte";
3352 case "System.Int16": return "short";
3353 case "System.Int32": return "int";
3354 case "System.Int64": return "long";
3356 case "System.UInt16": return "ushort";
3357 case "System.UInt32": return "uint";
3358 case "System.UInt64": return "ulong";
3360 case "System.Single": return "float";
3361 case "System.Double": return "double";
3362 case "System.Decimal": return "decimal";
3363 case "System.Boolean": return "bool";
3364 case "System.Char": return "char";
3365 case "System.Void": return "void";
3366 case "System.String": return "string";
3367 case "System.Object": return "object";
3372 protected override StringBuilder AppendTypeName (StringBuilder buf, Type type)
3374 if (DocUtils.IsGenericParameter (type))
3375 return buf.Append (type.Name);
3376 string t = type.FullName;
3377 if (!t.StartsWith ("System.")) {
3378 return base.AppendTypeName (buf, type);
3381 string s = GetCSharpType (t);
3383 return buf.Append (s);
3385 return base.AppendTypeName (buf, type);
3388 protected override string GetTypeDeclaration (Type type)
3390 string visibility = GetTypeVisibility (type.Attributes);
3391 if (visibility == null)
3394 StringBuilder buf = new StringBuilder ();
3396 buf.Append (visibility);
3399 MemberFormatter full = new CSharpFullMemberFormatter ();
3401 if (IsDelegate(type)) {
3402 buf.Append("delegate ");
3403 MethodInfo invoke = type.GetMethod ("Invoke");
3404 buf.Append (full.GetName (invoke.ReturnType)).Append (" ");
3405 buf.Append (GetName (type));
3406 AppendParameters (buf, invoke, invoke.GetParameters ());
3407 AppendGenericTypeConstraints (buf, type);
3410 return buf.ToString();
3413 if (type.IsAbstract && !type.IsInterface)
3414 buf.Append("abstract ");
3415 if (type.IsSealed && !IsDelegate(type) && !type.IsValueType)
3416 buf.Append("sealed ");
3417 buf.Replace ("abstract sealed", "static");
3419 buf.Append (GetTypeKind (type));
3421 buf.Append (GetCSharpType (type.FullName) == null
3426 Type basetype = type.BaseType;
3427 if (basetype == typeof(object) || type.IsValueType) // don't show this in signatures
3430 ArrayList interface_names = new ArrayList ();
3431 foreach (Type i in type.GetInterfaces ())
3432 if ((type.BaseType == null || Array.IndexOf (type.BaseType.GetInterfaces (), i) == -1) &&
3433 InterfaceNotFromAnother (i, type.GetInterfaces ()))
3434 interface_names.Add (full.GetName (i));
3435 interface_names.Sort ();
3437 if (basetype != null || interface_names.Count > 0)
3440 if (basetype != null) {
3441 buf.Append (full.GetName (basetype));
3442 if (interface_names.Count > 0)
3446 for (int i = 0; i < interface_names.Count; i++){
3449 buf.Append (interface_names [i]);
3451 AppendGenericTypeConstraints (buf, type);
3454 return buf.ToString ();
3457 static string GetTypeKind (Type t)
3461 if (t.IsClass || t == typeof(System.Enum))
3467 throw new ArgumentException(t.FullName);
3470 static string GetTypeVisibility (TypeAttributes ta)
3472 switch (ta & TypeAttributes.VisibilityMask) {
3473 case TypeAttributes.Public:
3474 case TypeAttributes.NestedPublic:
3477 case TypeAttributes.NestedFamily:
3478 case TypeAttributes.NestedFamORAssem:
3486 static bool IsDelegate(Type type)
3488 return typeof (System.Delegate).IsAssignableFrom (type) && !type.IsAbstract;
3491 private static bool InterfaceNotFromAnother(Type i, Type[] i2)
3493 foreach (Type t in i2)
3494 if (i != t && Array.IndexOf (t.GetInterfaces(), i) != -1)
3499 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, Type type)
3501 if (!DocUtils.GetContainsGenericParameters (type))
3503 return AppendConstraints (buf, DocUtils.GetGenericArguments (type));
3506 private StringBuilder AppendConstraints (StringBuilder buf, Type[] genArgs)
3509 foreach (Type genArg in genArgs) {
3510 GenericParameterAttributes attrs = genArg.GenericParameterAttributes;
3511 Type[] constraints = genArg.GetGenericParameterConstraints ();
3512 if (attrs == GenericParameterAttributes.None && constraints.Length == 0)
3514 buf.Append (" where ").Append (genArg.Name).Append (" : ");
3515 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
3516 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
3517 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
3520 buf.Append ("class");
3524 buf.Append ("struct");
3527 if (constraints.Length > 0 && !isvt) {
3530 buf.Append (GetTypeName (constraints [0]));
3531 for (int i = 1; i < constraints.Length; ++i)
3532 buf.Append (", ").Append (GetTypeName (constraints [i]));
3534 if (isnew && !isvt) {
3537 buf.Append ("new()");
3544 protected override string GetConstructorDeclaration (ConstructorInfo constructor)
3546 StringBuilder buf = new StringBuilder ();
3547 AppendVisibility (buf, constructor);
3548 if (buf.Length == 0)
3552 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
3553 AppendParameters (buf, constructor, constructor.GetParameters ());
3556 return buf.ToString ();
3559 protected override string GetMethodDeclaration (MethodInfo method)
3561 string decl = base.GetMethodDeclaration (method);
3567 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodBase method)
3569 if (DocUtils.IsExplicitlyImplemented (method)) {
3571 MethodInfo ifaceMethod;
3572 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3573 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3575 .Append (ifaceMethod.Name);
3577 return base.AppendMethodName (buf, method);
3580 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodInfo method)
3582 if (!DocUtils.GetContainsGenericParameters (method))
3584 return AppendConstraints (buf, DocUtils.GetGenericArguments (method));
3587 protected override string RefTypeModifier {
3591 protected override string GetFinalizerName (MethodInfo method)
3593 return "~" + method.DeclaringType.Name + " ()";
3596 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodBase method)
3600 if (method.IsPublic)
3601 return buf.Append ("public");
3602 if (method.IsFamily || method.IsFamilyOrAssembly)
3603 return buf.Append ("protected");
3607 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodInfo method)
3609 string modifiers = String.Empty;
3610 if (method.IsStatic) modifiers += " static";
3611 if (method.IsVirtual && !method.IsAbstract) {
3612 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3613 else modifiers += " override";
3615 if (method.IsAbstract && !method.DeclaringType.IsInterface) modifiers += " abstract";
3616 if (method.IsFinal) modifiers += " sealed";
3617 if (modifiers == " virtual sealed") modifiers = "";
3619 return buf.Append (modifiers);
3622 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodInfo method)
3624 if (DocUtils.GetContainsGenericParameters (method)) {
3625 Type[] args = DocUtils.GetGenericArguments (method);
3626 if (args.Length > 0) {
3628 buf.Append (args [0].Name);
3629 for (int i = 1; i < args.Length; ++i)
3630 buf.Append (",").Append (args [i].Name);
3637 protected override StringBuilder AppendParameters (StringBuilder buf, MethodBase method, ParameterInfo[] parameters)
3639 return AppendParameters (buf, method, parameters, '(', ')');
3642 private StringBuilder AppendParameters (StringBuilder buf, MethodBase method, ParameterInfo[] parameters, char begin, char end)
3646 if (parameters.Length > 0) {
3647 if (DocUtils.IsExtensionMethod (method))
3648 buf.Append ("this ");
3649 AppendParameter (buf, parameters [0]);
3650 for (int i = 1; i < parameters.Length; ++i) {
3652 AppendParameter (buf, parameters [i]);
3656 return buf.Append (end);
3659 private StringBuilder AppendParameter (StringBuilder buf, ParameterInfo parameter)
3661 if (parameter.ParameterType.IsByRef) {
3662 if (parameter.IsOut)
3663 buf.Append ("out ");
3665 buf.Append ("ref ");
3667 buf.Append (GetName (parameter.ParameterType)).Append (" ");
3668 return buf.Append (parameter.Name);
3671 protected override string GetPropertyDeclaration (PropertyInfo property)
3675 string get_visible = null;
3676 if ((method = property.GetGetMethod (true)) != null &&
3677 (DocUtils.IsExplicitlyImplemented (method) ||
3678 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3679 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3680 string set_visible = null;
3681 if ((method = property.GetSetMethod (true)) != null &&
3682 (DocUtils.IsExplicitlyImplemented (method) ||
3683 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3684 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3686 if ((set_visible == null) && (get_visible == null))
3690 StringBuilder buf = new StringBuilder ();
3691 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
3692 buf.Append (visibility = get_visible);
3693 else if (set_visible != null && get_visible == null)
3694 buf.Append (visibility = set_visible);
3696 buf.Append (visibility = "public");
3698 // Pick an accessor to use for static/virtual/override/etc. checks.
3699 method = property.GetSetMethod (true);
3701 method = property.GetGetMethod (true);
3703 string modifiers = String.Empty;
3704 if (method.IsStatic) modifiers += " static";
3705 if (method.IsVirtual && !method.IsAbstract) {
3706 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
3707 modifiers += " virtual";
3709 modifiers += " override";
3711 if (method.IsAbstract && !method.DeclaringType.IsInterface)
3712 modifiers += " abstract";
3714 modifiers += " sealed";
3715 if (modifiers == " virtual sealed")
3717 buf.Append (modifiers).Append (' ');
3719 buf.Append (GetName (property.PropertyType)).Append (' ');
3721 MemberInfo[] defs = property.DeclaringType.GetDefaultMembers ();
3722 string name = property.Name;
3723 foreach (MemberInfo mi in defs) {
3724 if (mi == property) {
3729 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
3731 if (property.GetIndexParameters ().Length != 0) {
3732 AppendParameters (buf, method, property.GetIndexParameters (), '[', ']');
3736 if (set_visible != null) {
3737 if (set_visible != visibility)
3738 buf.Append (' ').Append (set_visible);
3739 buf.Append (" set;");
3741 if (get_visible != null) {
3742 if (get_visible != visibility)
3743 buf.Append (' ').Append (get_visible);
3744 buf.Append (" get;");
3748 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
3751 protected override string GetFieldDeclaration (FieldInfo field)
3753 if (field.DeclaringType.IsEnum && field.Name == "value__")
3754 return null; // This member of enums aren't documented.
3756 StringBuilder buf = new StringBuilder ();
3757 AppendFieldVisibility (buf, field);
3758 if (buf.Length == 0)
3761 if (field.DeclaringType.IsEnum)
3764 if (field.IsStatic && !field.IsLiteral)
3765 buf.Append (" static");
3766 if (field.IsInitOnly)
3767 buf.Append (" readonly");
3768 if (field.IsLiteral)
3769 buf.Append (" const");
3771 buf.Append (' ').Append (GetName (field.FieldType)).Append (' ');
3772 buf.Append (field.Name);
3773 AppendFieldValue (buf, field);
3776 return buf.ToString ();
3779 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldInfo field)
3782 return buf.Append ("public");
3783 if (field.IsFamily || field.IsFamilyOrAssembly)
3784 return buf.Append ("protected");
3788 static StringBuilder AppendFieldValue (StringBuilder buf, FieldInfo field)
3790 // enums have a value__ field, which we ignore, and FieldInfo.GetValue()
3791 // on a GenericType results in InvalidOperationException
3792 if (field.DeclaringType.IsEnum ||
3793 DocUtils.IsGenericType (field.DeclaringType))
3795 if (field.IsLiteral || (field.IsStatic && field.IsInitOnly)) {
3798 val = field.GetValue (null);
3803 buf.Append (" = ").Append ("null");
3804 else if (val is Enum)
3805 buf.Append (" = ").Append (val.ToString ());
3806 else if (val is IFormattable) {
3807 string value = ((IFormattable)val).ToString();
3809 value = "\"" + value + "\"";
3810 buf.Append (" = ").Append (value);
3816 protected override string GetEventDeclaration (EventInfo e)
3818 StringBuilder buf = new StringBuilder ();
3819 if (AppendVisibility (buf, e.GetAddMethod (true)).Length == 0) {
3823 AppendModifiers (buf, e.GetAddMethod (true));
3825 buf.Append (" event ");
3826 buf.Append (GetName (e.EventHandlerType)).Append (' ');
3827 buf.Append (e.Name).Append (';');
3829 return buf.ToString ();
3833 class CSharpMemberFormatter : CSharpFullMemberFormatter {
3834 protected override StringBuilder AppendNamespace (StringBuilder buf, Type type)
3840 class DocTypeFullMemberFormatter : MemberFormatter {
3841 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
3843 protected override char NestedTypeSeparator {
3848 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
3849 protected override StringBuilder AppendNamespace (StringBuilder buf, Type type)
3855 class SlashDocMemberFormatter : MemberFormatter {
3857 protected override char[] GenericTypeContainer {
3858 get {return new char[]{'{', '}'};}
3861 private bool AddTypeCount = true;
3863 protected override string GetTypeName (Type type)
3865 return base.GetTypeName (type);
3868 private Type genDeclType;
3869 private MethodBase genDeclMethod;
3871 protected override StringBuilder AppendTypeName (StringBuilder buf, Type type)
3873 if (DocUtils.IsGenericParameter (type)) {
3875 if (genDeclType != null) {
3876 Type[] genArgs = DocUtils.GetGenericArguments (genDeclType);
3877 for (int i = 0; i < genArgs.Length; ++i) {
3878 if (genArgs [i].Name == type.Name) {
3879 buf.Append ('`').Append (i);
3884 if (genDeclMethod != null) {
3885 Type[] genArgs = null;
3886 if (DocUtils.GetContainsGenericParameters (genDeclMethod)) {
3887 genArgs = DocUtils.GetGenericArguments (genDeclMethod);
3890 genArgs = new Type[0];
3891 for (int i = 0; i < genArgs.Length; ++i) {
3892 if (genArgs [i].Name == type.Name) {
3893 buf.Append ("``").Append (i);
3898 if (genDeclType == null && genDeclMethod == null) {
3899 // Probably from within an explicitly implemented interface member,
3900 // where CSC uses parameter names instead of indices (why?), e.g.
3901 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
3902 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
3903 buf.Append (type.Name);
3905 if (buf.Length == l) {
3906 throw new Exception (string.Format (
3907 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
3908 type.Name, genDeclType, genDeclMethod));
3912 base.AppendTypeName (buf, type);
3914 int numArgs = DocUtils.GetGenericArguments (type).Length;
3915 if (type.DeclaringType != null)
3916 numArgs -= DocUtils.GetGenericArguments (type).Length;
3918 buf.Append ('`').Append (numArgs);
3925 protected override StringBuilder AppendGenericType (StringBuilder buf, Type type)
3928 base.AppendGenericType (buf, type);
3930 AppendType (buf, type);
3934 private StringBuilder AppendType (StringBuilder buf, Type type)
3936 int numArgs = DocUtils.GetGenericArguments (type).Length;
3937 if (type.DeclaringType != null) {
3938 AppendType (buf, type.DeclaringType).Append (NestedTypeSeparator);
3939 numArgs -= DocUtils.GetGenericArguments (type.DeclaringType).Length;
3941 base.AppendTypeName (buf, type);
3943 buf.Append ('`').Append (numArgs);
3948 protected override string GetConstructorName (ConstructorInfo constructor)
3950 return GetMethodBaseName (constructor, "#ctor");
3953 protected override string GetMethodName (MethodInfo method)
3956 if (!DocUtils.IsExplicitlyImplemented (method))
3960 MethodInfo ifaceMethod;
3961 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3962 AddTypeCount = false;
3963 name = GetTypeName (iface) + "." + ifaceMethod.Name;
3964 AddTypeCount = true;
3966 return GetMethodBaseName (method, name);
3969 private string GetMethodBaseName (MethodBase method, string name)
3971 StringBuilder buf = new StringBuilder ();
3972 buf.Append (GetTypeName (method.DeclaringType));
3974 buf.Append (name.Replace (".", "#"));
3975 if (DocUtils.GetContainsGenericParameters (method)) {
3976 Type[] genArgs = DocUtils.GetGenericArguments (method);
3977 if (genArgs.Length > 0)
3978 buf.Append ("``").Append (genArgs.Length);
3980 ParameterInfo[] parameters = method.GetParameters ();
3981 genDeclType = method.DeclaringType;
3982 genDeclMethod = method;
3983 AppendParameters (buf, DocUtils.GetGenericArguments (method.DeclaringType), parameters);
3985 genDeclMethod = null;
3986 return buf.ToString ();
3989 private StringBuilder AppendParameters (StringBuilder buf, Type[] genArgs, ParameterInfo[] parameters)
3991 if (parameters.Length == 0)
3996 AppendParameter (buf, genArgs, parameters [0]);
3997 for (int i = 1; i < parameters.Length; ++i) {
3999 AppendParameter (buf, genArgs, parameters [i]);
4002 return buf.Append (')');
4005 private StringBuilder AppendParameter (StringBuilder buf, Type[] genArgs, ParameterInfo parameter)
4007 AddTypeCount = false;
4008 buf.Append (GetTypeName (parameter.ParameterType));
4009 AddTypeCount = true;
4013 protected override string GetPropertyName (PropertyInfo property)
4017 MethodInfo method = property.GetGetMethod (true);
4019 method = property.GetSetMethod (true);
4020 if (!DocUtils.IsExplicitlyImplemented (method))
4021 name = property.Name;
4024 MethodInfo ifaceMethod;
4025 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4026 AddTypeCount = false;
4027 name = string.Join ("#", new string[]{
4028 GetTypeName (iface).Replace (".", "#"),
4029 DocUtils.GetMember (property.Name)
4031 AddTypeCount = true;
4034 StringBuilder buf = new StringBuilder ();
4035 buf.Append (GetName (property.DeclaringType));
4038 ParameterInfo[] parameters = property.GetIndexParameters ();
4039 if (parameters.Length > 0) {
4040 genDeclType = property.DeclaringType;
4042 Type[] genArgs = DocUtils.GetGenericArguments (property.DeclaringType);
4043 AppendParameter (buf, genArgs, parameters [0]);
4044 for (int i = 1; i < parameters.Length; ++i) {
4046 AppendParameter (buf, genArgs, parameters [i]);
4051 return buf.ToString ();
4054 protected override string GetFieldName (FieldInfo field)
4056 return string.Format ("{0}.{1}",
4057 GetName (field.DeclaringType), field.Name);
4060 protected override string GetEventName (EventInfo e)
4062 return string.Format ("{0}.{1}",
4063 GetName (e.DeclaringType), e.Name);
4066 protected override string GetTypeDeclaration (Type type)
4068 string name = GetName (type);
4074 protected override string GetConstructorDeclaration (ConstructorInfo constructor)
4076 string name = GetName (constructor);
4082 protected override string GetMethodDeclaration (MethodInfo method)
4084 string name = GetName (method);
4087 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4088 genDeclType = method.DeclaringType;
4089 genDeclMethod = method;
4090 name += "~" + GetName (method.ReturnType);
4092 genDeclMethod = null;
4097 protected override string GetPropertyDeclaration (PropertyInfo property)
4099 string name = GetName (property);
4105 protected override string GetFieldDeclaration (FieldInfo field)
4107 string name = GetName (field);
4113 protected override string GetEventDeclaration (EventInfo e)
4115 string name = GetName (e);
4122 class FileNameMemberFormatter : SlashDocMemberFormatter {
4123 protected override StringBuilder AppendNamespace (StringBuilder buf, Type type)
4128 protected override char NestedTypeSeparator {