1 // Updater program for syncing Mono's ECMA-style documentation files
3 // By Joshua Tauberer <tauberer@for.net>
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Diagnostics;
9 using System.Globalization;
14 using System.Xml.XPath;
19 using MyXmlNodeList = System.Collections.Generic.List<System.Xml.XmlNode>;
20 using StringList = System.Collections.Generic.List<string>;
21 using StringToStringMap = System.Collections.Generic.Dictionary<string, string>;
22 using StringToXmlNodeMap = System.Collections.Generic.Dictionary<string, System.Xml.XmlNode>;
24 namespace Mono.Documentation {
26 class MDocUpdater : MDocCommand
29 List<AssemblyDefinition> assemblies;
30 readonly DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver();
34 bool no_assembly_versions, ignore_missing_types;
35 ExceptionLocations? exceptions;
37 internal int additions = 0, deletions = 0;
39 List<DocumentationImporter> importers = new List<DocumentationImporter> ();
41 DocumentationEnumerator docEnum;
45 static readonly MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
46 static readonly MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
48 static MemberFormatter[] typeFormatters = new MemberFormatter[]{
49 new CSharpMemberFormatter (),
50 new ILMemberFormatter (),
53 static MemberFormatter[] memberFormatters = new MemberFormatter[]{
54 new CSharpFullMemberFormatter (),
55 new ILFullMemberFormatter (),
58 internal static readonly MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
60 MyXmlNodeList extensionMethods = new MyXmlNodeList ();
62 public override void Run (IEnumerable<string> args)
64 show_exceptions = DebugOutput;
66 var types = new List<string> ();
67 var p = new OptionSet () {
69 "Delete removed members from the XML files.",
70 v => delete = v != null },
72 "Document potential exceptions that members can generate. {SOURCES} " +
73 "is a comma-separated list of:\n" +
74 " asm Method calls in same assembly\n" +
75 " depasm Method calls in dependent assemblies\n" +
76 " all Record all possible exceptions\n" +
77 " added Modifier; only create <exception/>s\n" +
78 " for NEW types/members\n" +
79 "If nothing is specified, then only exceptions from the member will " +
81 v => exceptions = ParseExceptionLocations (v) },
83 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
86 case "ignore-missing-types":
87 ignore_missing_types = true;
89 case "no-assembly-versions":
90 no_assembly_versions = true;
93 throw new Exception ("Unsupported flag `" + v + "'.");
96 { "fignore-missing-types",
97 "Do not report an error if a --type=TYPE type\nwas not found.",
98 v => ignore_missing_types = v != null },
99 { "fno-assembly-versions",
100 "Do not generate //AssemblyVersion elements.",
101 v => no_assembly_versions = v != null },
103 "Import documentation from {FILE}.",
104 v => AddImporter (v) },
106 "Check for assembly references in {DIRECTORY}.",
107 v => assemblyResolver.AddSearchDirectory (v) },
109 "Ignored for compatibility with update-ecma-xml.",
112 "Root {DIRECTORY} to generate/update documentation.",
115 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
116 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
117 v => assemblyResolver.AddSearchDirectory (Path.GetDirectoryName (v)) },
119 "Manually specify the assembly {VERSION} that new members were added in.",
122 "Only update documentation for {TYPE}.",
123 v => types.Add (v) },
125 var assemblies = Parse (p, args, "update",
126 "[OPTIONS]+ ASSEMBLIES",
127 "Create or update documentation from ASSEMBLIES.");
128 if (assemblies == null)
130 if (assemblies.Count == 0)
131 Error ("No assemblies specified.");
133 foreach (var dir in assemblies
134 .Where (a => a.Contains (Path.DirectorySeparatorChar))
135 .Select (a => Path.GetDirectoryName (a)))
136 assemblyResolver.AddSearchDirectory (dir);
138 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
141 throw new InvalidOperationException("The --out option is required.");
143 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
145 docEnum = docEnum ?? new DocumentationEnumerator ();
147 // PERFORM THE UPDATES
149 if (types.Count > 0) {
151 DoUpdateTypes (srcPath, types, srcPath);
154 else if (opts.@namespace != null)
155 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
156 Path.Combine (dest_dir, opts.@namespace));
159 DoUpdateAssemblies (srcPath, srcPath);
161 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
164 void AddImporter (string path)
167 XmlReader r = new XmlTextReader (path);
169 while (r.NodeType != XmlNodeType.Element) {
171 Error ("Unable to read XML file: {0}.", path);
173 if (r.LocalName == "doc") {
174 importers.Add (new MsxdocDocumentationImporter (path));
176 else if (r.LocalName == "Libraries") {
177 var ecmadocs = new XmlTextReader (path);
178 docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
179 importers.Add (new EcmaDocumentationImporter (ecmadocs));
182 Error ("Unsupported XML format within {0}.", path);
185 } catch (Exception e) {
186 Environment.ExitCode = 1;
187 Error ("Could not load XML file: {0}.", e.Message);
191 static ExceptionLocations ParseExceptionLocations (string s)
193 ExceptionLocations loc = ExceptionLocations.Member;
196 foreach (var type in s.Split (',')) {
198 case "added": loc |= ExceptionLocations.AddedMembers; break;
199 case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
200 case "asm": loc |= ExceptionLocations.Assembly; break;
201 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
202 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
208 internal void Warning (string format, params object[] args)
210 Message (TraceLevel.Warning, "mdoc: " + format, args);
213 private AssemblyDefinition LoadAssembly (string name)
215 AssemblyDefinition assembly = null;
217 assembly = AssemblyFactory.GetAssembly (name);
218 } catch (System.IO.FileNotFoundException) { }
220 if (assembly == null)
221 throw new InvalidOperationException("Assembly " + name + " not found.");
223 assembly.Resolver = assemblyResolver;
227 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
228 OrderTypeAttributes (element);
229 XmlTextWriter writer = new XmlTextWriter(output);
230 writer.Formatting = Formatting.Indented;
231 writer.Indentation = 2;
232 writer.IndentChar = ' ';
233 element.WriteTo(writer);
237 private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
239 Action<string> creator = file => {
240 using (var writer = OpenWrite (file, mode))
244 MdocFile.UpdateFile (filename, creator);
247 private static void OrderTypeAttributes (XmlElement e)
249 foreach (XmlElement type in e.SelectNodes ("//Type")) {
250 OrderTypeAttributes (type.Attributes);
254 static readonly string[] TypeAttributeOrder = {
255 "Name", "FullName", "FullNameSP", "Maintainer"
258 private static void OrderTypeAttributes (XmlAttributeCollection c)
260 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
261 for (int i = 0; i < c.Count; ++i) {
262 XmlAttribute a = c [i];
263 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
264 if (a.Name == TypeAttributeOrder [j]) {
270 for (int i = attrs.Length-1; i >= 0; --i) {
271 XmlAttribute n = attrs [i];
274 XmlAttribute r = null;
275 for (int j = i+1; j < attrs.Length; ++j) {
276 if (attrs [j] != null) {
284 c.InsertBefore (n, r);
288 private XmlDocument CreateIndexStub()
290 XmlDocument index = new XmlDocument();
292 XmlElement index_root = index.CreateElement("Overview");
293 index.AppendChild(index_root);
295 if (assemblies.Count == 0)
296 throw new Exception ("No assembly");
298 XmlElement index_assemblies = index.CreateElement("Assemblies");
299 index_root.AppendChild(index_assemblies);
301 XmlElement index_remarks = index.CreateElement("Remarks");
302 index_remarks.InnerText = "To be added.";
303 index_root.AppendChild(index_remarks);
305 XmlElement index_copyright = index.CreateElement("Copyright");
306 index_copyright.InnerText = "To be added.";
307 index_root.AppendChild(index_copyright);
309 XmlElement index_types = index.CreateElement("Types");
310 index_root.AppendChild(index_types);
315 private static void WriteNamespaceStub(string ns, string outdir) {
316 XmlDocument index = new XmlDocument();
318 XmlElement index_root = index.CreateElement("Namespace");
319 index.AppendChild(index_root);
321 index_root.SetAttribute("Name", ns);
323 XmlElement index_docs = index.CreateElement("Docs");
324 index_root.AppendChild(index_docs);
326 XmlElement index_summary = index.CreateElement("summary");
327 index_summary.InnerText = "To be added.";
328 index_docs.AppendChild(index_summary);
330 XmlElement index_remarks = index.CreateElement("remarks");
331 index_remarks.InnerText = "To be added.";
332 index_docs.AppendChild(index_remarks);
334 WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
335 writer => WriteXml (index.DocumentElement, writer));
338 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
340 var index = CreateIndexForTypes (dest);
342 var found = new HashSet<string> ();
343 foreach (AssemblyDefinition assembly in assemblies) {
344 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames)) {
345 string relpath = DoUpdateType (type, basepath, dest);
349 found.Add (type.FullName);
354 index.Add (assembly);
362 if (ignore_missing_types)
365 var notFound = from n in typenames where !found.Contains (n) select n;
367 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
370 class IndexForTypes {
376 XmlElement index_types;
377 XmlElement index_assemblies;
379 public IndexForTypes (MDocUpdater app, string indexFile, XmlDocument index)
382 this.indexFile = indexFile;
385 index_types = WriteElement (index.DocumentElement, "Types");
386 index_assemblies = WriteElement (index.DocumentElement, "Assemblies");
389 public void Add (AssemblyDefinition assembly)
391 if (index_assemblies.SelectSingleNode ("Assembly[@Name='" + assembly.Name.Name + "']") != null)
394 app.AddIndexAssembly (assembly, index_assemblies);
397 public void Add (TypeDefinition type)
399 app.AddIndexType (type, index_types);
404 SortIndexEntries (index_types);
405 WriteFile (indexFile, FileMode.Create,
406 writer => WriteXml (index.DocumentElement, writer));
410 IndexForTypes CreateIndexForTypes (string dest)
412 string indexFile = Path.Combine (dest, "index.xml");
413 if (File.Exists (indexFile))
415 return new IndexForTypes (this, indexFile, CreateIndexStub ());
418 public string DoUpdateType (TypeDefinition type, string basepath, string dest)
420 if (type.Namespace == null)
421 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
423 if (!IsPublic (type))
426 // Must get the A+B form of the type name.
427 string typename = GetTypeFileName(type);
429 string reltypefile = DocUtils.PathCombine (DocUtils.GetNamespace (type), typename + ".xml");
430 string typefile = Path.Combine (basepath, reltypefile);
431 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
433 string output = null;
436 } else if (dest == "-") {
439 output = Path.Combine (dest, reltypefile);
444 XmlDocument basefile = new XmlDocument();
446 basefile.Load(typefile);
447 } catch (Exception e) {
448 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
451 DoUpdateType2("Updating", basefile, type, output, false);
454 XmlElement td = StubType(type, output);
458 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
461 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
467 public void DoUpdateNS (string ns, string nspath, string outpath)
469 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
470 AssemblyDefinition assembly = assemblies [0];
472 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
473 XmlDocument basefile = new XmlDocument();
474 string typefile = Path.Combine(nspath, file.Name);
476 basefile.Load(typefile);
477 } catch (Exception e) {
478 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
482 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
483 TypeDefinition type = assembly.GetType(typename);
485 Warning ("Type no longer in assembly: " + typename);
489 seenTypes[type] = seenTypes;
490 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false);
493 // Stub types not in the directory
494 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
495 if (type.Namespace != ns || seenTypes.ContainsKey(type))
498 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"));
499 if (td == null) continue;
503 private static string GetTypeFileName (TypeReference type)
505 return filenameFormatter.GetName (type);
508 public static string GetTypeFileName (string typename)
510 StringBuilder filename = new StringBuilder (typename.Length);
514 for (int i = 0; i < typename.Length; ++i) {
515 char c = typename [i];
524 filename.Append ('`').Append ((numArgs+1).ToString());
539 return filename.ToString ();
542 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
544 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
545 index_assembly.SetAttribute ("Name", assembly.Name.Name);
546 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
548 AssemblyNameDefinition name = assembly.Name;
549 if (name.HasPublicKey) {
550 XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
551 var key = new StringBuilder (name.PublicKey.Length*3 + 2);
553 foreach (byte b in name.PublicKey)
554 key.AppendFormat ("{0,2:x2} ", b);
556 pubkey.InnerText = key.ToString ();
557 index_assembly.AppendChild (pubkey);
560 if (!string.IsNullOrEmpty (name.Culture)) {
561 XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
562 culture.InnerText = name.Culture;
563 index_assembly.AppendChild (culture);
566 MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
567 parent.AppendChild(index_assembly);
570 private void AddIndexType (TypeDefinition type, XmlElement index_types)
572 string typename = GetTypeFileName(type);
574 // Add namespace and type nodes into the index file as needed
575 string ns = DocUtils.GetNamespace (type);
576 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode ("Namespace[@Name='" + ns + "']");
577 if (nsnode == null) {
578 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
579 nsnode.SetAttribute ("Name", ns);
580 index_types.AppendChild (nsnode);
582 string doc_typename = GetDocTypeName (type);
583 XmlElement typenode = (XmlElement) nsnode.SelectSingleNode ("Type[@Name='" + typename + "']");
584 if (typenode == null) {
585 typenode = index_types.OwnerDocument.CreateElement ("Type");
586 typenode.SetAttribute ("Name", typename);
587 nsnode.AppendChild (typenode);
589 if (typename != doc_typename)
590 typenode.SetAttribute("DisplayName", doc_typename);
592 typenode.RemoveAttribute("DisplayName");
594 typenode.SetAttribute ("Kind", GetTypeKind (type));
597 private void DoUpdateAssemblies (string source, string dest)
599 string indexfile = dest + "/index.xml";
601 if (System.IO.File.Exists(indexfile)) {
602 index = new XmlDocument();
603 index.Load(indexfile);
606 ClearElement(index.DocumentElement, "Assembly");
607 ClearElement(index.DocumentElement, "Attributes");
609 index = CreateIndexStub();
612 string defaultTitle = "Untitled";
613 if (assemblies.Count == 1)
614 defaultTitle = assemblies[0].Name.Name;
615 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
617 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
618 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
619 index_assemblies.RemoveAll ();
622 HashSet<string> goodfiles = new HashSet<string> ();
624 foreach (AssemblyDefinition assm in assemblies) {
625 AddIndexAssembly (assm, index_assemblies);
626 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
629 SortIndexEntries (index_types);
631 CleanupFiles (dest, goodfiles);
632 CleanupIndexTypes (index_types, goodfiles);
633 CleanupExtensions (index_types);
635 WriteFile (indexfile, FileMode.Create,
636 writer => WriteXml(index.DocumentElement, writer));
639 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
641 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
643 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
644 string typename = GetTypeFileName(type);
645 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0)
648 string reltypepath = DoUpdateType (type, source, dest);
649 if (reltypepath == null)
652 // Add namespace and type nodes into the index file as needed
653 AddIndexType (type, index_types);
655 // Ensure the namespace index file exists
656 string onsdoc = DocUtils.PathCombine (dest, type.Namespace + ".xml");
657 string nsdoc = DocUtils.PathCombine (dest, "ns-" + type.Namespace + ".xml");
658 if (File.Exists (onsdoc)) {
659 File.Move (onsdoc, nsdoc);
662 if (!File.Exists (nsdoc)) {
663 Console.WriteLine("New Namespace File: " + type.Namespace);
664 WriteNamespaceStub(type.Namespace, dest);
667 goodfiles.Add (reltypepath);
671 private static void SortIndexEntries (XmlElement indexTypes)
673 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
674 XmlNodeComparer c = new AttributeNameComparer ();
675 SortXmlNodes (indexTypes, namespaces, c);
677 for (int i = 0; i < namespaces.Count; ++i)
678 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
681 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
683 MyXmlNodeList l = new MyXmlNodeList (children.Count);
684 for (int i = 0; i < children.Count; ++i)
685 l.Add (children [i]);
687 for (int i = l.Count - 1; i > 0; --i) {
688 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
692 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
694 public abstract int Compare (XmlNode x, XmlNode y);
696 public int Compare (object x, object y)
698 return Compare ((XmlNode) x, (XmlNode) y);
702 class AttributeNameComparer : XmlNodeComparer {
705 public AttributeNameComparer ()
710 public AttributeNameComparer (string attribute)
712 this.attribute = attribute;
715 public override int Compare (XmlNode x, XmlNode y)
717 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
721 class VersionComparer : XmlNodeComparer {
722 public override int Compare (XmlNode x, XmlNode y)
724 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
725 string a = GetVersion (x.InnerText);
726 string b = GetVersion (y.InnerText);
727 return new Version (a).CompareTo (new Version (b));
730 static string GetVersion (string v)
732 int n = v.IndexOf ("x");
735 return v.Substring (0, n-1);
739 private static string GetTypeKind (TypeDefinition type)
742 return "Enumeration";
743 if (type.IsValueType)
745 if (type.IsInterface)
747 if (DocUtils.IsDelegate (type))
749 if (type.IsClass || type.FullName == "System.Enum") // FIXME
751 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
754 private static bool IsPublic (TypeDefinition type)
756 TypeDefinition decl = type;
757 while (decl != null) {
758 if (!(decl.IsPublic || decl.IsNestedPublic)) {
761 decl = (TypeDefinition) decl.DeclaringType;
766 private void CleanupFiles (string dest, HashSet<string> goodfiles)
768 // Look for files that no longer correspond to types
769 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
770 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
771 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
772 if (!goodfiles.Contains (relTypeFile)) {
773 XmlDocument doc = new XmlDocument ();
774 doc.Load (typefile.FullName);
775 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
776 if (UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
777 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
778 WriteXml(doc.DocumentElement, writer);
779 goodfiles.Add (relTypeFile);
782 string newname = typefile.FullName + ".remove";
783 try { System.IO.File.Delete(newname); } catch (Exception) { }
784 try { typefile.MoveTo(newname); } catch (Exception) { }
785 Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
791 private static TextWriter OpenWrite (string path, FileMode mode)
793 var w = new StreamWriter (
794 new FileStream (path, mode),
795 new UTF8Encoding (false)
801 private string[] GetAssemblyVersions ()
803 return (from a in assemblies select GetAssemblyVersion (a)).ToArray ();
806 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
808 // Look for type nodes that no longer correspond to types
809 MyXmlNodeList remove = new MyXmlNodeList ();
810 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
811 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
812 if (!goodfiles.Contains (fulltypename)) {
813 remove.Add (typenode);
816 foreach (XmlNode n in remove)
817 n.ParentNode.RemoveChild (n);
820 private void CleanupExtensions (XmlElement index_types)
822 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
823 if (extensionMethods.Count == 0) {
826 index_types.SelectSingleNode ("/Overview").RemoveChild (e);
830 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
831 index_types.SelectSingleNode ("/Overview").AppendChild (e);
835 extensionMethods.Sort (DefaultExtensionMethodComparer);
836 foreach (XmlNode m in extensionMethods) {
837 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
841 class ExtensionMethodComparer : XmlNodeComparer {
842 public override int Compare (XmlNode x, XmlNode y)
844 XmlNode xLink = x.SelectSingleNode ("Member/Link");
845 XmlNode yLink = y.SelectSingleNode ("Member/Link");
847 int n = xLink.Attributes ["Type"].Value.CompareTo (
848 yLink.Attributes ["Type"].Value);
851 n = xLink.Attributes ["Member"].Value.CompareTo (
852 yLink.Attributes ["Member"].Value);
853 if (n == 0 && !object.ReferenceEquals (x, y))
854 throw new InvalidOperationException ("Duplicate extension method found!");
859 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
861 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince)
863 Console.WriteLine(message + ": " + type.FullName);
865 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
867 // Update type metadata
868 UpdateType(basefile.DocumentElement, type);
870 // Update existing members. Delete member nodes that no longer should be there,
871 // and remember what members are already documented so we don't add them again.
873 MyXmlNodeList todelete = new MyXmlNodeList ();
874 foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
875 XmlElement oldmember = info.Node;
876 IMemberReference oldmember2 = info.Member;
877 string sig = oldmember2 != null ? memberFormatters [0].GetDeclaration (oldmember2) : null;
879 // Interface implementations and overrides are deleted from the docs
880 // unless the overrides option is given.
881 if (oldmember2 != null && sig == null)
884 // Deleted (or signature changed)
885 if (oldmember2 == null) {
886 if (UpdateAssemblyVersions (oldmember, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
888 DeleteMember ("Member Removed", output, oldmember, todelete);
893 if (seenmembers.ContainsKey (sig)) {
894 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
895 // ignore, already seen
897 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
898 DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
900 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
904 // Update signature information
907 seenmembers.Add (sig, oldmember);
909 foreach (XmlElement oldmember in todelete)
910 oldmember.ParentNode.RemoveChild (oldmember);
913 if (!DocUtils.IsDelegate (type)) {
914 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
915 foreach (IMemberReference m in type.GetMembers()) {
916 if (m is TypeDefinition) continue;
918 string sig = memberFormatters [0].GetDeclaration (m);
919 if (sig == null) continue;
920 if (seenmembers.ContainsKey(sig)) continue;
922 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
923 if (mm == null) continue;
924 members.AppendChild( mm );
926 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
931 // Import code snippets from files
932 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
933 if (!(code is XmlElement)) continue;
934 string file = ((XmlElement)code).GetAttribute("src");
935 string lang = ((XmlElement)code).GetAttribute("lang");
937 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
939 code.InnerText = src;
943 if (insertSince && since != null) {
944 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
945 docs.AppendChild (CreateSinceNode (basefile));
949 XmlElement d = basefile.DocumentElement ["Docs"];
950 XmlElement m = basefile.DocumentElement ["Members"];
951 if (d != null && m != null)
952 basefile.DocumentElement.InsertBefore (
953 basefile.DocumentElement.RemoveChild (d), m);
958 WriteXml(basefile.DocumentElement, Console.Out);
960 FileInfo file = new FileInfo (output);
961 if (!file.Directory.Exists) {
962 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
963 file.Directory.Create ();
965 WriteFile (output, FileMode.Create,
966 writer => WriteXml(basefile.DocumentElement, writer));
970 private string GetCodeSource (string lang, string file)
973 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
974 // Grab the specified region
975 string region = "#region " + file.Substring (anchorStart + 4);
976 file = file.Substring (0, anchorStart + 3);
978 using (StreamReader reader = new StreamReader (file)) {
980 StringBuilder src = new StringBuilder ();
982 while ((line = reader.ReadLine ()) != null) {
983 if (line.Trim() == region) {
984 indent = line.IndexOf (region);
987 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
992 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
995 return src.ToString ();
997 } catch (Exception e) {
998 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
999 file, region, show_exceptions ? e.ToString () : e.Message);
1004 using (StreamReader reader = new StreamReader (file))
1005 return reader.ReadToEnd ();
1006 } catch (Exception e) {
1007 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
1012 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
1014 string format = output != null
1015 ? "{0}: File='{1}'; Signature='{4}'"
1016 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1020 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1021 member.Attributes ["MemberName"].Value,
1022 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
1023 if (!delete && MemberDocsHaveUserContent (member)) {
1024 Warning ("Member deletions must be enabled with the --delete option.");
1026 todelete.Add (member);
1031 class MemberComparer : XmlNodeComparer {
1032 public override int Compare (XmlNode x, XmlNode y)
1035 string xMemberName = x.Attributes ["MemberName"].Value;
1036 string yMemberName = y.Attributes ["MemberName"].Value;
1038 // generic methods *end* with '>'
1039 // it's possible for explicitly implemented generic interfaces to
1040 // contain <...> without being a generic method
1041 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1042 (r = xMemberName.CompareTo (yMemberName)) != 0)
1046 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1047 xMemberName = xMemberName.Substring (0, lt);
1048 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1049 yMemberName = yMemberName.Substring (0, lt);
1050 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1053 // if @MemberName matches, then it's either two different types of
1054 // members sharing the same name, e.g. field & property, or it's an
1055 // overloaded method.
1056 // for different type, sort based on MemberType value.
1057 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1058 y.SelectSingleNode ("MemberType").InnerText);
1062 // same type -- must be an overloaded method. Sort based on type
1063 // parameter count, then parameter count, then by the parameter
1065 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1066 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1067 if (xTypeParams.Count != yTypeParams.Count)
1068 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1069 for (int i = 0; i < xTypeParams.Count; ++i) {
1070 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1071 yTypeParams [i].Attributes ["Name"].Value);
1076 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1077 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1078 if (xParams.Count != yParams.Count)
1079 return xParams.Count <= yParams.Count ? -1 : 1;
1080 for (int i = 0; i < xParams.Count; ++i) {
1081 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1082 yParams [i].Attributes ["Type"].Value);
1086 // all parameters match, but return value might not match if it was
1087 // changed between one version and another.
1088 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1089 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1090 if (xReturn != null && yReturn != null) {
1091 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1100 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1102 private static void SortTypeMembers (XmlNode members)
1104 if (members == null)
1106 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1109 private static bool MemberDocsHaveUserContent (XmlNode e)
1111 e = (XmlElement)e.SelectSingleNode("Docs");
1112 if (e == null) return false;
1113 foreach (XmlElement d in e.SelectNodes("*"))
1114 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1119 // UPDATE HELPER FUNCTIONS
1121 // CREATE A STUB DOCUMENTATION FILE
1123 public XmlElement StubType (TypeDefinition type, string output)
1125 string typesig = typeFormatters [0].GetDeclaration (type);
1126 if (typesig == null) return null; // not publicly visible
1128 XmlDocument doc = new XmlDocument();
1129 XmlElement root = doc.CreateElement("Type");
1130 doc.AppendChild (root);
1132 DoUpdateType2 ("New Type", doc, type, output, true);
1137 private XmlElement CreateSinceNode (XmlDocument doc)
1139 XmlElement s = doc.CreateElement ("since");
1140 s.SetAttribute ("version", since);
1144 // STUBBING/UPDATING FUNCTIONS
1146 public void UpdateType (XmlElement root, TypeDefinition type)
1148 root.SetAttribute("Name", GetDocTypeName (type));
1149 root.SetAttribute("FullName", GetDocTypeFullName (type));
1151 foreach (MemberFormatter f in typeFormatters) {
1152 string element = "TypeSignature[@Language='" + f.Language + "']";
1153 WriteElementAttribute (root, element, "Language", f.Language);
1154 WriteElementAttribute (root, element, "Value", f.GetDeclaration (type));
1157 XmlElement ass = WriteElement(root, "AssemblyInfo");
1158 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1159 if (!no_assembly_versions) {
1160 UpdateAssemblyVersions (root, type, true);
1163 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1164 foreach (var version in versions)
1165 ass.RemoveChild (version);
1167 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1168 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1170 ClearElement(ass, "AssemblyCulture");
1172 // Why-oh-why do we put assembly attributes in each type file?
1173 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1174 // since they're outdated in current docs, and a waste of space.
1175 //MakeAttributes(ass, type.Assembly, true);
1176 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1177 if (assattrs != null)
1178 ass.RemoveChild(assattrs);
1180 NormalizeWhitespace(ass);
1182 if (type.IsGenericType ()) {
1183 MakeTypeParameters (root, type.GenericParameters);
1185 ClearElement(root, "TypeParameters");
1188 if (type.BaseType != null) {
1189 XmlElement basenode = WriteElement(root, "Base");
1191 string basetypename = GetDocTypeFullName (type.BaseType);
1192 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1193 WriteElementText(root, "Base/BaseTypeName", basetypename);
1195 // Document how this type instantiates the generic parameters of its base type
1196 TypeReference origBase = type.BaseType.GetOriginalType ();
1197 if (origBase.IsGenericType ()) {
1198 ClearElement(basenode, "BaseTypeArguments");
1199 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1200 GenericArgumentCollection baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1201 GenericParameterCollection baseGenParams = origBase.GenericParameters;
1202 if (baseGenArgs.Count != baseGenParams.Count)
1203 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1204 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1205 GenericParameter param = baseGenParams [i];
1206 TypeReference value = baseGenArgs [i];
1208 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1209 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1210 bta.AppendChild(arg);
1211 arg.SetAttribute ("TypeParamName", param.Name);
1212 arg.InnerText = GetDocTypeFullName (value);
1216 ClearElement(root, "Base");
1219 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1220 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1221 List<string> interface_names = userInterfaces
1222 .Select (iface => GetDocTypeFullName (iface))
1226 XmlElement interfaces = WriteElement(root, "Interfaces");
1227 interfaces.RemoveAll();
1228 foreach (string iname in interface_names) {
1229 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1230 interfaces.AppendChild(iface);
1231 WriteElementText(iface, "InterfaceName", iname);
1234 ClearElement(root, "Interfaces");
1237 MakeAttributes (root, GetCustomAttributes (type));
1239 if (DocUtils.IsDelegate (type)) {
1240 MakeTypeParameters (root, type.GenericParameters);
1241 MakeParameters(root, type.GetMethod("Invoke").Parameters);
1242 MakeReturnValue(root, type.GetMethod("Invoke"));
1245 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1246 MakeDocNode (typeInfo);
1248 if (!DocUtils.IsDelegate (type))
1249 WriteElement (root, "Members");
1251 OrderTypeNodes (root, root.ChildNodes);
1252 NormalizeWhitespace(root);
1255 static readonly string[] TypeNodeOrder = {
1259 "ThreadingSafetyStatement",
1260 "ThreadSafetyStatement",
1272 static void OrderTypeNodes (XmlNode member, XmlNodeList children)
1274 ReorderNodes (member, children, TypeNodeOrder);
1277 internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1279 List<T> l = new List<T> (list);
1284 private void UpdateMember (DocsNodeInfo info)
1286 XmlElement me = (XmlElement) info.Node;
1287 IMemberReference mi = info.Member;
1289 foreach (MemberFormatter f in memberFormatters) {
1290 string element = "MemberSignature[@Language='" + f.Language + "']";
1291 WriteElementAttribute (me, element, "Language", f.Language);
1292 WriteElementAttribute (me, element, "Value", f.GetDeclaration (mi));
1295 WriteElementText(me, "MemberType", GetMemberType(mi));
1297 if (!no_assembly_versions) {
1298 UpdateAssemblyVersions (me, mi, true);
1301 ClearElement (me, "AssemblyInfo");
1304 MakeAttributes (me, GetCustomAttributes (mi));
1306 MakeReturnValue(me, mi);
1307 if (mi is MethodReference) {
1308 MethodReference mb = (MethodReference) mi;
1309 if (mb.IsGenericMethod ())
1310 MakeTypeParameters (me, mb.GenericParameters);
1312 MakeParameters(me, mi);
1315 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1316 WriteElementText(me, "MemberValue", fieldValue);
1318 info.Node = WriteElement (me, "Docs");
1320 OrderMemberNodes (me, me.ChildNodes);
1321 UpdateExtensionMethods (me, info);
1324 static readonly string[] MemberNodeOrder = {
1339 static void OrderMemberNodes (XmlNode member, XmlNodeList children)
1341 ReorderNodes (member, children, MemberNodeOrder);
1344 static void ReorderNodes (XmlNode node, XmlNodeList children, string[] ordering)
1346 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1347 for (int i = 0; i < ordering.Length; ++i) {
1348 for (int j = 0; j < children.Count; ++j) {
1349 XmlNode c = children [j];
1350 if (c.Name == ordering [i]) {
1351 newChildren.Add (c);
1355 if (newChildren.Count >= 0)
1356 node.PrependChild ((XmlNode) newChildren [0]);
1357 for (int i = 1; i < newChildren.Count; ++i) {
1358 XmlNode prev = (XmlNode) newChildren [i-1];
1359 XmlNode cur = (XmlNode) newChildren [i];
1360 node.RemoveChild (cur);
1361 node.InsertAfter (cur, prev);
1365 IEnumerable<string> GetCustomAttributes (IMemberReference mi)
1367 IEnumerable<string> attrs = Enumerable.Empty<string>();
1369 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1371 attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1373 PropertyReference pr = mi as PropertyReference;
1375 PropertyDefinition pd = pr.Resolve ();
1376 if (pd.GetMethod != null)
1377 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1378 if (pd.SetMethod != null)
1379 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1382 EventReference er = mi as EventReference;
1384 EventDefinition ed = er.Resolve ();
1385 if (ed.AddMethod != null)
1386 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1387 if (ed.RemoveMethod != null)
1388 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1394 IEnumerable<string> GetCustomAttributes (CustomAttributeCollection attributes, string prefix)
1396 foreach (CustomAttribute attribute in attributes.Cast<CustomAttribute> ()
1397 .OrderBy (ca => ca.Constructor.DeclaringType.FullName)) {
1398 if (!attribute.Resolve ()) {
1400 Warning ("warning: could not resolve type {0}.",
1401 attribute.Constructor.DeclaringType.FullName);
1403 TypeDefinition attrType = attribute.Constructor.DeclaringType as TypeDefinition;
1404 if (attrType != null && !IsPublic (attrType))
1406 if (slashdocFormatter.GetName (attribute.Constructor.DeclaringType) == null)
1409 if (Array.IndexOf (IgnorableAttributes, attribute.Constructor.DeclaringType.FullName) >= 0)
1412 StringList fields = new StringList ();
1414 ParameterDefinitionCollection parameters = attribute.Constructor.Parameters;
1415 for (int i = 0; i < attribute.ConstructorParameters.Count; ++i) {
1416 fields.Add (MakeAttributesValueString (
1417 attribute.ConstructorParameters [i],
1418 parameters [i].ParameterType));
1421 (from de in attribute.Fields.Cast<DictionaryEntry> ()
1422 select new { Type=attribute.GetFieldType (de.Key.ToString ()), Name=de.Key, Value=de.Value })
1424 (from de in attribute.Properties.Cast<DictionaryEntry> ()
1425 select new { Type=attribute.GetPropertyType (de.Key.ToString ()), Name=de.Key, Value=de.Value }))
1426 .OrderBy (v => v.Name);
1427 foreach (var d in namedArgs)
1428 fields.Add (string.Format ("{0}={1}", d.Name,
1429 MakeAttributesValueString (d.Value, d.Type)));
1431 string a2 = String.Join(", ", fields.ToArray ());
1432 if (a2 != "") a2 = "(" + a2 + ")";
1434 string name = attribute.Constructor.DeclaringType.FullName;
1435 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1436 yield return prefix + name + a2;
1440 static readonly string[] ValidExtensionMembers = {
1449 static readonly string[] ValidExtensionDocMembers = {
1455 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1457 MethodDefinition me = info.Member as MethodDefinition;
1460 if (info.Parameters.Count < 1)
1462 if (!DocUtils.IsExtensionMethod (me))
1465 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1466 XmlNode member = e.CloneNode (true);
1467 em.AppendChild (member);
1468 RemoveExcept (member, ValidExtensionMembers);
1469 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1470 WriteElementText (member, "MemberType", "ExtensionMethod");
1471 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1472 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1473 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1474 member.AppendChild (link);
1475 AddTargets (em, info);
1477 extensionMethods.Add (em);
1480 private static void RemoveExcept (XmlNode node, string[] except)
1484 MyXmlNodeList remove = null;
1485 foreach (XmlNode n in node.ChildNodes) {
1486 if (Array.BinarySearch (except, n.Name) < 0) {
1488 remove = new MyXmlNodeList ();
1493 foreach (XmlNode n in remove)
1494 node.RemoveChild (n);
1497 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1499 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1500 member.PrependChild (targets);
1501 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1502 AppendElementAttributeText (targets, "Target", "Type",
1503 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1506 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1507 ConstraintCollection constraints = gp.Constraints;
1508 if (constraints.Count == 0)
1509 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1511 foreach (TypeReference c in constraints)
1512 AppendElementAttributeText(targets, "Target", "Type",
1513 slashdocFormatter.GetDeclaration (c));
1517 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1520 TypeDefinition type = field.DeclaringType.Resolve ();
1521 if (type != null && type.IsEnum) return false;
1523 if (type != null && type.IsGenericType ()) return false;
1524 if (!field.HasConstant)
1526 if (field.IsLiteral) {
1527 object val = field.Constant;
1528 if (val == null) value = "null";
1529 else if (val is Enum) value = val.ToString();
1530 else if (val is IFormattable) {
1531 value = ((IFormattable)val).ToString();
1533 value = "\"" + value + "\"";
1535 if (value != null && value != "")
1541 // XML HELPER FUNCTIONS
1543 internal static XmlElement WriteElement(XmlNode parent, string element) {
1544 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1546 string[] path = element.Split('/');
1547 foreach (string p in path) {
1548 ret = (XmlElement)parent.SelectSingleNode(p);
1551 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1552 ename = ename.Substring(0, ename.IndexOf('['));
1553 ret = parent.OwnerDocument.CreateElement(ename);
1554 parent.AppendChild(ret);
1563 private static void WriteElementText(XmlNode parent, string element, string value) {
1564 XmlElement node = WriteElement(parent, element);
1565 node.InnerText = value;
1568 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1570 XmlElement n = parent.OwnerDocument.CreateElement (element);
1571 parent.AppendChild (n);
1572 n.InnerText = value;
1576 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1578 XmlElement n = parent.OwnerDocument.CreateElement (element);
1579 parent.AppendChild (n);
1580 n.SetAttribute (attribute, value);
1584 internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
1586 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1587 dest.AppendChild (copy);
1591 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1592 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1595 node = WriteElement(parent, element);
1596 node.InnerText = value;
1598 private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
1599 XmlElement node = WriteElement(parent, element);
1600 if (node.GetAttribute(attribute) == value) return;
1601 node.SetAttribute(attribute, value);
1603 internal static void ClearElement(XmlElement parent, string name) {
1604 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1606 parent.RemoveChild(node);
1609 // DOCUMENTATION HELPER FUNCTIONS
1611 private void MakeDocNode (DocsNodeInfo info)
1613 List<GenericParameter> genericParams = info.GenericParameters;
1614 ParameterDefinitionCollection parameters = info.Parameters;
1615 TypeReference returntype = info.ReturnType;
1616 bool returnisreturn = info.ReturnIsReturn;
1617 XmlElement e = info.Node;
1618 bool addremarks = info.AddRemarks;
1620 WriteElementInitialText(e, "summary", "To be added.");
1622 if (parameters != null) {
1623 string[] values = new string [parameters.Count];
1624 for (int i = 0; i < values.Length; ++i)
1625 values [i] = parameters [i].Name;
1626 UpdateParameters (e, "param", values);
1629 if (genericParams != null) {
1630 string[] values = new string [genericParams.Count];
1631 for (int i = 0; i < values.Length; ++i)
1632 values [i] = genericParams [i].Name;
1633 UpdateParameters (e, "typeparam", values);
1636 string retnodename = null;
1637 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
1638 retnodename = returnisreturn ? "returns" : "value";
1639 string retnodename_other = !returnisreturn ? "returns" : "value";
1641 // If it has a returns node instead of a value node, change its name.
1642 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1643 if (retother != null) {
1644 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1645 foreach (XmlNode node in retother)
1646 retnode.AppendChild(node.CloneNode(true));
1647 e.ReplaceChild(retnode, retother);
1649 WriteElementInitialText(e, retnodename, "To be added.");
1652 ClearElement(e, "returns");
1653 ClearElement(e, "value");
1657 WriteElementInitialText(e, "remarks", "To be added.");
1659 if (exceptions.HasValue && info.Member != null &&
1660 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
1661 UpdateExceptions (e, info.Member);
1664 foreach (DocumentationImporter importer in importers)
1665 importer.ImportDocumentation (info);
1667 OrderDocsNodes (e, e.ChildNodes);
1668 NormalizeWhitespace(e);
1671 static readonly string[] DocsNodeOrder = {
1672 "typeparam", "param", "summary", "returns", "value", "remarks",
1675 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
1677 ReorderNodes (docs, children, DocsNodeOrder);
1681 private void UpdateParameters (XmlElement e, string element, string[] values)
1683 if (values != null) {
1684 XmlNode[] paramnodes = new XmlNode[values.Length];
1686 // Some documentation had param nodes with leading spaces.
1687 foreach (XmlElement paramnode in e.SelectNodes(element)){
1688 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
1691 // If a member has only one parameter, we can track changes to
1692 // the name of the parameter easily.
1693 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
1694 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
1697 bool reinsert = false;
1699 // Pick out existing and still-valid param nodes, and
1700 // create nodes for parameters not in the file.
1701 Hashtable seenParams = new Hashtable();
1702 for (int pi = 0; pi < values.Length; pi++) {
1703 string p = values [pi];
1706 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
1707 if (paramnodes[pi] != null) continue;
1709 XmlElement pe = e.OwnerDocument.CreateElement(element);
1710 pe.SetAttribute("name", p);
1711 pe.InnerText = "To be added.";
1712 paramnodes[pi] = pe;
1716 // Remove parameters that no longer exist and check all params are in the right order.
1718 MyXmlNodeList todelete = new MyXmlNodeList ();
1719 foreach (XmlElement paramnode in e.SelectNodes(element)) {
1720 string name = paramnode.GetAttribute("name");
1721 if (!seenParams.ContainsKey(name)) {
1722 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1723 Warning ("The following param node can only be deleted if the --delete option is given: ");
1724 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
1726 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
1727 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1731 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
1732 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1733 e.ParentNode.Attributes ["MemberName"].Value,
1736 Warning ("\tValue={0}", paramnode.OuterXml);
1738 todelete.Add (paramnode);
1743 if ((int)seenParams[name] != idx)
1749 foreach (XmlNode n in todelete) {
1750 n.ParentNode.RemoveChild (n);
1753 // Re-insert the parameter nodes at the top of the doc section.
1755 for (int pi = values.Length-1; pi >= 0; pi--)
1756 e.PrependChild(paramnodes[pi]);
1758 // Clear all existing param nodes
1759 foreach (XmlNode paramnode in e.SelectNodes(element)) {
1760 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1761 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
1762 Console.WriteLine(paramnode.OuterXml);
1764 paramnode.ParentNode.RemoveChild(paramnode);
1770 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
1772 string existingName = pe.GetAttribute ("name");
1773 pe.SetAttribute ("name", newName);
1774 if (existingName == newName)
1776 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
1777 if (paramref.GetAttribute ("name").Trim () == existingName)
1778 paramref.SetAttribute ("name", newName);
1781 class CrefComparer : XmlNodeComparer {
1783 public CrefComparer ()
1787 public override int Compare (XmlNode x, XmlNode y)
1789 string xType = x.Attributes ["cref"].Value;
1790 string yType = y.Attributes ["cref"].Value;
1791 string xNamespace = GetNamespace (xType);
1792 string yNamespace = GetNamespace (yType);
1794 int c = xNamespace.CompareTo (yNamespace);
1797 return xType.CompareTo (yType);
1800 static string GetNamespace (string type)
1802 int n = type.LastIndexOf ('.');
1804 return type.Substring (0, n);
1805 return string.Empty;
1809 private void UpdateExceptions (XmlNode docs, IMemberReference member)
1811 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
1812 string cref = slashdocFormatter.GetDeclaration (source.Exception);
1813 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
1816 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
1817 e.SetAttribute ("cref", cref);
1818 e.InnerXml = "To be added; from: <see cref=\"" +
1819 string.Join ("\" />, <see cref=\"",
1820 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
1823 docs.AppendChild (e);
1825 SortXmlNodes (docs, docs.SelectNodes ("exception"),
1826 new CrefComparer ());
1829 private static void NormalizeWhitespace(XmlElement e) {
1830 // Remove all text and whitespace nodes from the element so it
1831 // is outputted with nice indentation and no blank lines.
1832 ArrayList deleteNodes = new ArrayList();
1833 foreach (XmlNode n in e)
1834 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
1836 foreach (XmlNode n in deleteNodes)
1837 n.ParentNode.RemoveChild(n);
1840 private static bool UpdateAssemblyVersions (XmlElement root, IMemberReference member, bool add)
1842 TypeDefinition type = member as TypeDefinition;
1844 type = member.DeclaringType as TypeDefinition;
1845 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
1848 private static string GetAssemblyVersion (AssemblyDefinition assembly)
1850 return assembly.Name.Version.ToString();
1853 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
1855 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
1857 e = root.OwnerDocument.CreateElement("AssemblyInfo");
1858 root.AppendChild(e);
1860 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
1861 .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
1863 // matches.Count > 0 && add: ignore -- already present
1864 if (matches.Count > 0 && !add) {
1865 foreach (XmlNode c in matches)
1868 else if (matches.Count == 0 && add) {
1869 foreach (string sv in assemblyVersions) {
1870 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
1875 // matches.Count == 0 && !add: ignore -- already not present
1877 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
1878 SortXmlNodes (e, avs, new VersionComparer ());
1880 return avs.Count != 0;
1883 // FIXME: get TypeReferences instead of string comparison?
1884 private static string[] IgnorableAttributes = {
1885 // Security related attributes
1886 "System.Reflection.AssemblyKeyFileAttribute",
1887 "System.Reflection.AssemblyDelaySignAttribute",
1888 // Present in @RefType
1889 "System.Runtime.InteropServices.OutAttribute",
1890 // For naming the indexer to use when not using indexers
1891 "System.Reflection.DefaultMemberAttribute",
1892 // for decimal constants
1893 "System.Runtime.CompilerServices.DecimalConstantAttribute",
1894 // compiler generated code
1895 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
1896 // more compiler generated code, e.g. iterator methods
1897 "System.Diagnostics.DebuggerHiddenAttribute",
1898 "System.Runtime.CompilerServices.FixedBufferAttribute",
1899 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
1900 // extension methods
1901 "System.Runtime.CompilerServices.ExtensionAttribute",
1902 // Used to differentiate 'object' from C#4 'dynamic'
1903 "System.Runtime.CompilerServices.DynamicAttribute",
1906 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes)
1908 if (!attributes.Any ()) {
1909 ClearElement (root, "Attributes");
1913 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
1917 e = root.OwnerDocument.CreateElement("Attributes");
1919 foreach (string attribute in attributes) {
1920 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
1923 WriteElementText(ae, "AttributeName", attribute);
1926 if (e.ParentNode == null)
1927 root.AppendChild(e);
1929 NormalizeWhitespace(e);
1932 private static string MakeAttributesValueString (object v, TypeReference valueType)
1936 if (valueType.FullName == "System.Type")
1937 return "typeof(" + v.ToString () + ")";
1938 if (valueType.FullName == "System.String")
1939 return "\"" + v.ToString () + "\"";
1941 return (bool)v ? "true" : "false";
1942 TypeDefinition valueDef = valueType.Resolve ();
1943 if (valueDef == null || !valueDef.IsEnum)
1944 return v.ToString ();
1945 string typename = GetDocTypeFullName (valueType);
1946 var values = GetEnumerationValues (valueDef);
1947 long c = ToInt64 (v);
1948 if (values.ContainsKey (c))
1949 return typename + "." + values [c];
1950 if (valueDef.CustomAttributes.Cast<CustomAttribute> ()
1951 .Any (ca => ca.Constructor.DeclaringType.FullName == "System.FlagsAttribute")) {
1952 return string.Join (" | ",
1953 (from i in values.Keys
1955 select typename + "." + values [i])
1958 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
1961 private static Dictionary<long, string> GetEnumerationValues (TypeDefinition type)
1963 var values = new Dictionary<long, string> ();
1965 (from f in type.Fields.Cast<FieldDefinition> ()
1966 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
1968 values [ToInt64 (f.Constant)] = f.Name;
1973 static long ToInt64 (object value)
1976 return (long) (ulong) value;
1977 return Convert.ToInt64 (value);
1980 private void MakeParameters (XmlElement root, ParameterDefinitionCollection parameters)
1982 XmlElement e = WriteElement(root, "Parameters");
1984 foreach (ParameterDefinition p in parameters) {
1985 XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
1987 pe.SetAttribute("Name", p.Name);
1988 pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
1989 if (p.ParameterType is ReferenceType) {
1990 if (p.IsOut) pe.SetAttribute("RefType", "out");
1991 else pe.SetAttribute("RefType", "ref");
1993 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
1997 private void MakeTypeParameters (XmlElement root, GenericParameterCollection typeParams)
1999 if (typeParams == null || typeParams.Count == 0) {
2000 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2002 root.RemoveChild (f);
2005 XmlElement e = WriteElement(root, "TypeParameters");
2007 foreach (GenericParameter t in typeParams) {
2008 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2010 pe.SetAttribute("Name", t.Name);
2011 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""));
2012 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2013 ConstraintCollection constraints = t.Constraints;
2014 GenericParameterAttributes attrs = t.Attributes;
2015 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2023 ce = root.OwnerDocument.CreateElement ("Constraints");
2025 pe.AppendChild (ce);
2026 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2027 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2028 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2029 AppendElementText (ce, "ParameterAttribute", "Covariant");
2030 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2031 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2032 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2033 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2034 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2035 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2036 foreach (TypeReference c in constraints) {
2037 TypeDefinition cd = c.Resolve ();
2038 AppendElementText (ce,
2039 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2040 GetDocTypeFullName (c));
2045 private void MakeParameters (XmlElement root, IMemberReference mi)
2047 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2048 MakeParameters (root, ((MethodDefinition)mi).Parameters);
2049 else if (mi is MethodDefinition) {
2050 MethodDefinition mb = (MethodDefinition) mi;
2051 ParameterDefinitionCollection parameters = mb.Parameters;
2052 MakeParameters(root, parameters);
2053 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2054 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2055 p.SetAttribute ("RefType", "this");
2058 else if (mi is PropertyDefinition) {
2059 ParameterDefinitionCollection parameters = ((PropertyDefinition)mi).Parameters;
2060 if (parameters.Count > 0)
2061 MakeParameters(root, parameters);
2065 else if (mi is FieldDefinition) return;
2066 else if (mi is EventDefinition) return;
2067 else throw new ArgumentException();
2070 internal static string GetDocParameterType (TypeReference type)
2072 return GetDocTypeFullName (type).Replace ("@", "&");
2075 private void MakeReturnValue (XmlElement root, TypeReference type, CustomAttributeCollection attributes)
2077 XmlElement e = WriteElement(root, "ReturnValue");
2079 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2080 if (attributes != null)
2081 MakeAttributes(e, GetCustomAttributes (attributes, ""));
2084 private void MakeReturnValue (XmlElement root, IMemberReference mi)
2086 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2088 else if (mi is MethodDefinition)
2089 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType.ReturnType, ((MethodDefinition)mi).ReturnType.CustomAttributes);
2090 else if (mi is PropertyDefinition)
2091 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
2092 else if (mi is FieldDefinition)
2093 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
2094 else if (mi is EventDefinition)
2095 MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
2097 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2100 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2102 IMemberReference mi = info.Member;
2103 if (mi is TypeDefinition) return null;
2105 string sigs = memberFormatters [0].GetDeclaration (mi);
2106 if (sigs == null) return null; // not publicly visible
2108 // no documentation for property/event accessors. Is there a better way of doing this?
2109 if (mi.Name.StartsWith("get_")) return null;
2110 if (mi.Name.StartsWith("set_")) return null;
2111 if (mi.Name.StartsWith("add_")) return null;
2112 if (mi.Name.StartsWith("remove_")) return null;
2113 if (mi.Name.StartsWith("raise_")) return null;
2115 XmlElement me = doc.CreateElement("Member");
2116 me.SetAttribute("MemberName", GetMemberName (mi));
2120 if (exceptions.HasValue &&
2121 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2122 UpdateExceptions (info.Node, info.Member);
2124 if (since != null) {
2125 XmlNode docs = me.SelectSingleNode("Docs");
2126 docs.AppendChild (CreateSinceNode (doc));
2132 internal static string GetMemberName (IMemberReference mi)
2134 MethodDefinition mb = mi as MethodDefinition;
2136 PropertyDefinition pi = mi as PropertyDefinition;
2139 return DocUtils.GetPropertyName (pi);
2141 StringBuilder sb = new StringBuilder (mi.Name.Length);
2142 if (!DocUtils.IsExplicitlyImplemented (mb))
2143 sb.Append (mi.Name);
2145 TypeReference iface;
2146 MethodReference ifaceMethod;
2147 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2148 sb.Append (GetDocTypeFullName (iface));
2150 sb.Append (ifaceMethod.Name);
2152 if (mb.IsGenericMethod ()) {
2153 GenericParameterCollection typeParams = mb.GenericParameters;
2154 if (typeParams.Count > 0) {
2156 sb.Append (typeParams [0].Name);
2157 for (int i = 1; i < typeParams.Count; ++i)
2158 sb.Append (",").Append (typeParams [i].Name);
2162 return sb.ToString ();
2165 /// SIGNATURE GENERATION FUNCTIONS
2166 internal static bool IsPrivate (IMemberReference mi)
2168 return memberFormatters [0].GetDeclaration (mi) == null;
2171 internal static string GetMemberType (IMemberReference mi)
2173 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2174 return "Constructor";
2175 if (mi is MethodDefinition)
2177 if (mi is PropertyDefinition)
2179 if (mi is FieldDefinition)
2181 if (mi is EventDefinition)
2183 throw new ArgumentException();
2186 private static string GetDocTypeName (TypeReference type)
2188 return docTypeFormatter.GetName (type);
2191 internal static string GetDocTypeFullName (TypeReference type)
2193 return DocTypeFullMemberFormatter.Default.GetName (type);
2196 internal static string GetXPathForMember (DocumentationMember member)
2198 StringBuilder xpath = new StringBuilder ();
2199 xpath.Append ("//Members/Member[@MemberName=\"")
2200 .Append (member.MemberName)
2202 if (member.Parameters != null && member.Parameters.Count > 0) {
2203 xpath.Append ("/Parameters[count(Parameter) = ")
2204 .Append (member.Parameters.Count);
2205 for (int i = 0; i < member.Parameters.Count; ++i) {
2206 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2207 xpath.Append (member.Parameters [i]);
2208 xpath.Append ("\"");
2210 xpath.Append ("]/..");
2212 return xpath.ToString ();
2215 public static string GetXPathForMember (XPathNavigator member)
2217 StringBuilder xpath = new StringBuilder ();
2218 xpath.Append ("//Type[@FullName=\"")
2219 .Append (member.SelectSingleNode ("../../@FullName").Value)
2221 xpath.Append ("Members/Member[@MemberName=\"")
2222 .Append (member.SelectSingleNode ("@MemberName").Value)
2224 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2225 if (parameters.Count > 0) {
2226 xpath.Append ("/Parameters[count(Parameter) = ")
2227 .Append (parameters.Count);
2229 while (parameters.MoveNext ()) {
2231 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2232 xpath.Append (parameters.Current.Value);
2233 xpath.Append ("\"");
2235 xpath.Append ("]/..");
2237 return xpath.ToString ();
2240 public static string GetXPathForMember (IMemberReference member)
2242 StringBuilder xpath = new StringBuilder ();
2243 xpath.Append ("//Type[@FullName=\"")
2244 .Append (member.DeclaringType.FullName)
2246 xpath.Append ("Members/Member[@MemberName=\"")
2247 .Append (GetMemberName (member))
2250 ParameterDefinitionCollection parameters = null;
2251 if (member is MethodDefinition)
2252 parameters = ((MethodDefinition) member).Parameters;
2253 else if (member is PropertyDefinition) {
2254 parameters = ((PropertyDefinition) member).Parameters;
2256 if (parameters != null && parameters.Count > 0) {
2257 xpath.Append ("/Parameters[count(Parameter) = ")
2258 .Append (parameters.Count);
2259 for (int i = 0; i < parameters.Count; ++i) {
2260 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2261 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2262 xpath.Append ("\"");
2264 xpath.Append ("]/..");
2266 return xpath.ToString ();
2270 static class CecilExtensions {
2271 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type)
2273 foreach (var c in type.Constructors)
2274 yield return (IMemberReference) c;
2275 foreach (var e in type.Events)
2276 yield return (IMemberReference) e;
2277 foreach (var f in type.Fields)
2278 yield return (IMemberReference) f;
2279 foreach (var m in type.Methods)
2280 yield return (IMemberReference) m;
2281 foreach (var t in type.NestedTypes)
2282 yield return (IMemberReference) t;
2283 foreach (var p in type.Properties)
2284 yield return (IMemberReference) p;
2287 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type, string member)
2289 return GetMembers (type).Where (m => m.Name == member);
2292 public static IMemberReference GetMember (this TypeDefinition type, string member)
2294 return GetMembers (type, member).EnsureZeroOrOne ();
2297 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2299 if (source.Count () > 1)
2300 throw new InvalidOperationException ("too many matches");
2301 return source.FirstOrDefault ();
2304 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2306 return type.Methods.Cast<MethodDefinition> ()
2307 .Where (m => m.Name == method)
2308 .EnsureZeroOrOne ();
2311 public static IEnumerable<IMemberReference> GetDefaultMembers (this TypeReference type)
2313 TypeDefinition def = type as TypeDefinition;
2315 return new IMemberReference [0];
2316 CustomAttribute defMemberAttr = type.CustomAttributes.Cast<CustomAttribute> ()
2317 .Where (c => c.Constructor.DeclaringType.FullName == "System.Reflection.DefaultMemberAttribute")
2319 if (defMemberAttr == null)
2320 return new IMemberReference [0];
2321 string name = (string) defMemberAttr.ConstructorParameters [0];
2322 return def.Properties.Cast<PropertyDefinition> ()
2323 .Where (p => p.Name == name)
2324 .Select (p => (IMemberReference) p);
2327 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2329 return assembly.Modules.Cast<ModuleDefinition> ()
2330 .SelectMany (md => md.Types.Cast<TypeDefinition> ());
2333 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2335 return GetTypes (assembly)
2336 .Where (td => td.FullName == type)
2337 .EnsureZeroOrOne ();
2340 public static bool IsGenericType (this TypeReference type)
2342 return type.GenericParameters.Count > 0;
2345 public static bool IsGenericMethod (this MethodReference method)
2347 return method.GenericParameters.Count > 0;
2350 public static IMemberReference Resolve (this IMemberReference member)
2352 EventReference er = member as EventReference;
2354 return er.Resolve ();
2355 FieldReference fr = member as FieldReference;
2357 return fr.Resolve ();
2358 MethodReference mr = member as MethodReference;
2360 return mr.Resolve ();
2361 PropertyReference pr = member as PropertyReference;
2363 return pr.Resolve ();
2364 TypeReference tr = member as TypeReference;
2366 return tr.Resolve ();
2367 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2370 public static TypeReference GetUnderlyingType (this TypeDefinition type)
2374 return type.Fields.Cast<FieldDefinition>().First (f => f.Name == "value__").FieldType;
2378 static class DocUtils {
2379 public static bool IsExplicitlyImplemented (MethodDefinition method)
2381 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2384 public static string GetTypeDotMember (string name)
2386 int startType, startMethod;
2387 startType = startMethod = -1;
2388 for (int i = 0; i < name.Length; ++i) {
2389 if (name [i] == '.') {
2390 startType = startMethod;
2394 return name.Substring (startType+1);
2397 public static string GetMember (string name)
2399 int i = name.LastIndexOf ('.');
2402 return name.Substring (i+1);
2405 public static void GetInfoForExplicitlyImplementedMethod (
2406 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2410 if (method.Overrides.Count != 1)
2411 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2412 iface = method.Overrides [0].DeclaringType;
2413 ifaceMethod = method.Overrides [0];
2416 public static string GetPropertyName (PropertyDefinition pi)
2418 // Issue: (g)mcs-generated assemblies that explicitly implement
2419 // properties don't specify the full namespace, just the
2420 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2421 MethodDefinition method = pi.GetMethod;
2423 method = pi.SetMethod;
2424 if (!IsExplicitlyImplemented (method))
2427 // Need to determine appropriate namespace for this member.
2428 TypeReference iface;
2429 MethodReference ifaceMethod;
2430 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2431 return string.Join (".", new string[]{
2432 DocTypeFullMemberFormatter.Default.GetName (iface),
2433 GetMember (pi.Name)});
2436 public static string GetNamespace (TypeReference type)
2438 if (type.GetOriginalType ().IsNested)
2439 type = type.GetOriginalType ();
2440 while (type != null && type.IsNested)
2441 type = type.DeclaringType;
2443 return string.Empty;
2444 return type.Namespace;
2447 public static string PathCombine (string dir, string path)
2453 return Path.Combine (dir, path);
2456 public static bool IsExtensionMethod (MethodDefinition method)
2459 method.CustomAttributes.Cast<CustomAttribute> ()
2460 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2462 method.DeclaringType.CustomAttributes.Cast<CustomAttribute> ()
2463 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2467 public static bool IsDelegate (TypeDefinition type)
2469 TypeReference baseRef = type.BaseType;
2470 if (baseRef == null)
2472 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2473 baseRef.FullName == "System.MulticastDelegate";
2476 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2478 List<TypeReference> decls = new List<TypeReference> ();
2480 while (type.DeclaringType != null) {
2481 decls.Add (type.DeclaringType);
2482 type = type.DeclaringType;
2488 public static int GetGenericArgumentCount (TypeReference type)
2490 GenericInstanceType inst = type as GenericInstanceType;
2492 ? inst.GenericArguments.Count
2493 : type.GenericParameters.Count;
2496 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
2498 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
2499 List<TypeReference> userInterfaces = new List<TypeReference> ();
2500 foreach (TypeReference iface in type.Interfaces) {
2501 TypeReference lookup = iface.Resolve () ?? iface;
2502 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
2503 userInterfaces.Add (iface);
2505 return userInterfaces;
2508 private static string GetQualifiedTypeName (TypeReference type)
2510 return "[" + type.Scope.Name + "]" + type.FullName;
2513 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
2515 HashSet<string> inheritedInterfaces = new HashSet<string> ();
2516 Action<TypeDefinition> a = null;
2518 if (t == null) return;
2519 foreach (TypeReference r in t.Interfaces) {
2520 inheritedInterfaces.Add (GetQualifiedTypeName (r));
2524 TypeReference baseRef = type.BaseType;
2525 while (baseRef != null) {
2526 TypeDefinition baseDef = baseRef.Resolve ();
2527 if (baseDef != null) {
2529 baseRef = baseDef.BaseType;
2534 foreach (TypeReference r in type.Interfaces)
2536 return inheritedInterfaces;
2540 class DocsNodeInfo {
2541 public DocsNodeInfo (XmlElement node)
2546 public DocsNodeInfo (XmlElement node, TypeDefinition type)
2552 public DocsNodeInfo (XmlElement node, IMemberReference member)
2555 SetMemberInfo (member);
2558 void SetType (TypeDefinition type)
2561 throw new ArgumentNullException ("type");
2563 GenericParameters = new List<GenericParameter> (type.GenericParameters.Cast<GenericParameter> ());
2564 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
2565 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
2566 for (int i = 0; i < declTypes.Count - 1; ++i) {
2567 int remove = System.Math.Min (maxGenArgs,
2568 DocUtils.GetGenericArgumentCount (declTypes [i]));
2569 maxGenArgs -= remove;
2570 while (remove-- > 0)
2571 GenericParameters.RemoveAt (0);
2573 if (DocUtils.IsDelegate (type)) {
2574 Parameters = type.GetMethod("Invoke").Parameters;
2575 ReturnType = type.GetMethod("Invoke").ReturnType.ReturnType;
2576 ReturnIsReturn = true;
2580 void SetMemberInfo (IMemberReference member)
2583 throw new ArgumentNullException ("member");
2584 ReturnIsReturn = true;
2588 if (member is MethodReference ) {
2589 MethodReference mr = (MethodReference) member;
2590 Parameters = mr.Parameters;
2591 if (mr.IsGenericMethod ()) {
2592 GenericParameters = new List<GenericParameter> (mr.GenericParameters.Cast<GenericParameter> ());
2595 else if (member is PropertyDefinition) {
2596 Parameters = ((PropertyDefinition) member).Parameters;
2599 if (member is MethodDefinition) {
2600 ReturnType = ((MethodDefinition) member).ReturnType.ReturnType;
2601 } else if (member is PropertyDefinition) {
2602 ReturnType = ((PropertyDefinition) member).PropertyType;
2603 ReturnIsReturn = false;
2606 // no remarks section for enum members
2607 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
2611 public TypeReference ReturnType;
2612 public List<GenericParameter> GenericParameters;
2613 public ParameterDefinitionCollection Parameters;
2614 public bool ReturnIsReturn;
2615 public XmlElement Node;
2616 public bool AddRemarks = true;
2617 public IMemberReference Member;
2618 public TypeDefinition Type;
2621 class DocumentationEnumerator {
2623 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2625 return GetDocumentationTypes (assembly, forTypes, null);
2628 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2630 foreach (TypeDefinition type in assembly.GetTypes()) {
2631 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
2633 if (seen != null && seen.Contains (type.FullName))
2636 foreach (TypeDefinition nested in type.NestedTypes)
2637 yield return nested;
2641 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2643 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
2644 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
2645 oldmember.RemoveAttribute ("__monodocer-seen__");
2648 IMemberReference m = GetMember (type, new DocumentationMember (oldmember));
2650 yield return new DocsNodeInfo (oldmember);
2653 yield return new DocsNodeInfo (oldmember, m);
2658 protected static IMemberReference GetMember (TypeDefinition type, DocumentationMember member)
2660 string membertype = member.MemberType;
2662 string returntype = member.ReturnType;
2664 string docName = member.MemberName;
2665 string[] docTypeParams = GetTypeParameters (docName);
2667 // Loop through all members in this type with the same name
2668 foreach (IMemberReference mi in GetReflectionMembers (type, docName)) {
2669 if (mi is TypeDefinition) continue;
2670 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
2672 if (MDocUpdater.IsPrivate (mi))
2675 ParameterDefinitionCollection pis = null;
2676 string[] typeParams = null;
2677 if (mi is MethodDefinition) {
2678 MethodDefinition mb = (MethodDefinition) mi;
2679 pis = mb.Parameters;
2680 if (docTypeParams != null && mb.IsGenericMethod ()) {
2681 GenericParameterCollection args = mb.GenericParameters;
2682 if (args.Count == docTypeParams.Length) {
2683 typeParams = args.Cast<GenericParameter> ().Select (p => p.Name).ToArray ();
2687 else if (mi is PropertyDefinition)
2688 pis = ((PropertyDefinition)mi).Parameters;
2690 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
2691 int pcount = pis == null ? 0 : pis.Count;
2692 if (mcount != pcount)
2695 MethodDefinition mDef = mi as MethodDefinition;
2696 if (mDef != null && !mDef.IsConstructor) {
2697 // Casting operators can overload based on return type.
2698 if (returntype != GetReplacedString (
2699 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType.ReturnType),
2700 typeParams, docTypeParams)) {
2708 for (int i = 0; i < pis.Count; i++) {
2709 string paramType = GetReplacedString (
2710 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
2711 typeParams, docTypeParams);
2712 if (paramType != (string) member.Parameters [i]) {
2717 if (!good) continue;
2725 static string[] GetTypeParameters (string docName)
2727 if (docName [docName.Length-1] != '>')
2729 StringList types = new StringList ();
2730 int endToken = docName.Length-2;
2731 int i = docName.Length-2;
2733 if (docName [i] == ',' || docName [i] == '<') {
2734 types.Add (docName.Substring (i + 1, endToken - i));
2737 if (docName [i] == '<')
2742 return types.ToArray ();
2745 protected static IEnumerable<IMemberReference> GetReflectionMembers (TypeDefinition type, string docName)
2747 // need to worry about 4 forms of //@MemberName values:
2748 // 1. "Normal" (non-generic) member names: GetEnumerator
2750 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
2751 // - try as-is, and try type.member (due to "kludge" for property
2753 // 3. "Normal" Generic member names: Sort<T> (CSC)
2754 // - need to remove generic parameters --> "Sort"
2755 // 4. Explicitly-implemented interface members for generic interfaces:
2756 // -- System.Collections.Generic.IEnumerable<T>.Current
2757 // - Try as-is, and try type.member, *keeping* the generic parameters.
2758 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
2759 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
2760 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
2761 // this as (1) or (2).
2762 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
2764 foreach (IMemberReference mi in type.GetMembers (docName))
2766 if (CountChars (docName, '.') > 0)
2767 // might be a property; try only type.member instead of
2768 // namespace.type.member.
2769 foreach (IMemberReference mi in
2770 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
2777 int startLt, startType, startMethod;
2778 startLt = startType = startMethod = -1;
2779 for (int i = 0; i < docName.Length; ++i) {
2780 switch (docName [i]) {
2789 if (numLt == 0 && (i + 1) < docName.Length)
2790 // there's another character in docName, so this <...> sequence is
2791 // probably part of a generic type -- case 4.
2795 startType = startMethod;
2801 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
2803 foreach (IMemberReference mi in type.GetMembers (refName))
2807 foreach (IMemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
2810 // If we _still_ haven't found it, we've hit another generic naming issue:
2811 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
2812 // explicitly-implemented METHOD names (not properties), e.g.
2813 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
2814 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
2815 // which the XML docs will contain.
2817 // Alas, we can't derive the Mono name from docName, so we need to iterate
2818 // over all member names, convert them into CSC format, and compare... :-(
2821 foreach (IMemberReference mi in type.GetMembers ()) {
2822 if (MDocUpdater.GetMemberName (mi) == docName)
2827 static string GetReplacedString (string typeName, string[] from, string[] to)
2831 for (int i = 0; i < from.Length; ++i)
2832 typeName = typeName.Replace (from [i], to [i]);
2836 private static int CountChars (string s, char c)
2839 for (int i = 0; i < s.Length; ++i) {
2847 class EcmaDocumentationEnumerator : DocumentationEnumerator {
2852 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
2855 this.ecmadocs = ecmaDocs;
2858 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2860 HashSet<string> seen = new HashSet<string> ();
2861 return GetDocumentationTypes (assembly, forTypes, seen)
2862 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
2865 IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2868 while (ecmadocs.Read ()) {
2869 switch (ecmadocs.Name) {
2871 if (typeDepth == -1)
2872 typeDepth = ecmadocs.Depth;
2873 if (ecmadocs.NodeType != XmlNodeType.Element)
2875 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
2877 string typename = ecmadocs.GetAttribute ("FullName");
2878 string typename2 = MDocUpdater.GetTypeFileName (typename);
2879 if (forTypes != null &&
2880 forTypes.BinarySearch (typename) < 0 &&
2881 typename != typename2 &&
2882 forTypes.BinarySearch (typename2) < 0)
2885 if ((t = assembly.GetType (typename)) == null &&
2886 (t = assembly.GetType (typename2)) == null)
2888 seen.Add (typename);
2889 if (typename != typename2)
2890 seen.Add (typename2);
2891 Console.WriteLine (" Import: {0}", t.FullName);
2892 if (ecmadocs.Name != "Docs") {
2893 int depth = ecmadocs.Depth;
2894 while (ecmadocs.Read ()) {
2895 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
2899 if (!ecmadocs.IsStartElement ("Docs"))
2900 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
2910 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2912 return GetMembers (basefile, type)
2913 .Concat (base.GetDocumentationMembers (basefile, type));
2916 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
2918 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
2921 if (ecmadocs.IsEmptyElement)
2924 int membersDepth = ecmadocs.Depth;
2926 while (go && ecmadocs.Read ()) {
2927 switch (ecmadocs.Name) {
2929 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
2931 DocumentationMember dm = new DocumentationMember (ecmadocs);
2932 string xp = MDocUpdater.GetXPathForMember (dm);
2933 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
2935 if (oldmember == null) {
2936 m = GetMember (type, dm);
2938 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2939 type.FullName, dm.MemberSignatures ["C#"]);
2940 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
2943 // oldmember lookup may have failed due to type parameter renames.
2945 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
2946 if (oldmember == null) {
2947 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
2948 oldmember = basefile.CreateElement ("Member");
2949 oldmember.SetAttribute ("MemberName", dm.MemberName);
2950 members.AppendChild (oldmember);
2951 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
2952 XmlElement ms = basefile.CreateElement ("MemberSignature");
2953 ms.SetAttribute ("Language", key);
2954 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
2955 oldmember.AppendChild (ms);
2957 oldmember.SetAttribute ("__monodocer-seen__", "true");
2958 Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
2963 m = GetMember (type, new DocumentationMember (oldmember));
2965 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2966 type.FullName, dm.MemberSignatures ["C#"]);
2969 oldmember.SetAttribute ("__monodocer-seen__", "true");
2971 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
2972 if (ecmadocs.Name != "Docs")
2973 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
2978 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
2987 abstract class DocumentationImporter {
2989 public abstract void ImportDocumentation (DocsNodeInfo info);
2992 class MsxdocDocumentationImporter : DocumentationImporter {
2994 XmlDocument slashdocs;
2996 public MsxdocDocumentationImporter (string file)
2998 var xml = File.ReadAllText (file);
3000 // Ensure Unix line endings
3001 xml = xml.Replace ("\r", "");
3003 slashdocs = new XmlDocument();
3004 slashdocs.LoadXml (xml);
3007 public override void ImportDocumentation (DocsNodeInfo info)
3009 XmlNode elem = GetDocs (info.Member ?? info.Type);
3014 XmlElement e = info.Node;
3016 if (elem.SelectSingleNode("summary") != null)
3017 MDocUpdater.ClearElement(e, "summary");
3018 if (elem.SelectSingleNode("remarks") != null)
3019 MDocUpdater.ClearElement(e, "remarks");
3020 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3021 MDocUpdater.ClearElement(e, "value");
3022 MDocUpdater.ClearElement(e, "returns");
3025 foreach (XmlNode child in elem.ChildNodes) {
3026 switch (child.Name) {
3029 XmlAttribute name = child.Attributes ["name"];
3032 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3034 p2.InnerXml = child.InnerXml;
3037 // Occasionally XML documentation will use <returns/> on
3038 // properties, so let's try to normalize things.
3041 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3042 v.InnerXml = child.InnerXml;
3048 case "permission": {
3049 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3052 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3054 a = e.OwnerDocument.CreateElement (child.Name);
3055 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
3058 a.InnerXml = child.InnerXml;
3062 XmlAttribute cref = child.Attributes ["cref"];
3065 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3067 a = e.OwnerDocument.CreateElement ("altmember");
3068 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
3075 if (child.NodeType == XmlNodeType.Element &&
3076 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3079 MDocUpdater.CopyNode (child, e);
3086 private XmlNode GetDocs (IMemberReference member)
3088 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3089 if (slashdocsig != null)
3090 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3095 class EcmaDocumentationImporter : DocumentationImporter {
3099 public EcmaDocumentationImporter (XmlReader ecmaDocs)
3101 this.ecmadocs = ecmaDocs;
3104 public override void ImportDocumentation (DocsNodeInfo info)
3106 if (!ecmadocs.IsStartElement ("Docs")) {
3110 XmlElement e = info.Node;
3112 int depth = ecmadocs.Depth;
3113 ecmadocs.ReadStartElement ("Docs");
3114 while (ecmadocs.Read ()) {
3115 if (ecmadocs.Name == "Docs") {
3116 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3119 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3121 if (!ecmadocs.IsStartElement ())
3123 switch (ecmadocs.Name) {
3126 string name = ecmadocs.GetAttribute ("name");
3129 XmlNode doc = e.SelectSingleNode (
3130 ecmadocs.Name + "[@name='" + name + "']");
3131 string value = ecmadocs.ReadInnerXml ();
3133 doc.InnerXml = value.Replace ("\r", "");
3140 string name = ecmadocs.Name;
3141 string cref = ecmadocs.GetAttribute ("cref");
3144 XmlNode doc = e.SelectSingleNode (
3145 ecmadocs.Name + "[@cref='" + cref + "']");
3146 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3148 doc.InnerXml = value;
3150 XmlElement n = e.OwnerDocument.CreateElement (name);
3151 n.SetAttribute ("cref", cref);
3158 string name = ecmadocs.Name;
3159 string xpath = ecmadocs.Name;
3160 StringList attributes = new StringList (ecmadocs.AttributeCount);
3161 if (ecmadocs.MoveToFirstAttribute ()) {
3163 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3164 } while (ecmadocs.MoveToNextAttribute ());
3165 ecmadocs.MoveToContent ();
3167 if (attributes.Count > 0) {
3168 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3170 XmlNode doc = e.SelectSingleNode (xpath);
3171 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3173 doc.InnerXml = value;
3176 XmlElement n = e.OwnerDocument.CreateElement (name);
3178 foreach (string a in attributes) {
3179 int eq = a.IndexOf ('=');
3180 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3191 class DocumentationMember {
3192 public StringToStringMap MemberSignatures = new StringToStringMap ();
3193 public string ReturnType;
3194 public StringList Parameters;
3195 public string MemberName;
3196 public string MemberType;
3198 public DocumentationMember (XmlReader reader)
3200 MemberName = reader.GetAttribute ("MemberName");
3201 int depth = reader.Depth;
3203 StringList p = new StringList ();
3205 if (reader.NodeType != XmlNodeType.Element)
3207 switch (reader.Name) {
3208 case "MemberSignature":
3209 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3212 MemberType = reader.ReadElementString ();
3215 if (reader.Depth == depth + 2)
3216 ReturnType = reader.ReadElementString ();
3219 if (reader.Depth == depth + 2)
3220 p.Add (reader.GetAttribute ("Type"));
3223 if (reader.Depth == depth + 1)
3227 } while (go && reader.Read () && reader.Depth >= depth);
3233 public DocumentationMember (XmlNode node)
3235 MemberName = node.Attributes ["MemberName"].Value;
3236 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3237 XmlAttribute l = n.Attributes ["Language"];
3238 XmlAttribute v = n.Attributes ["Value"];
3239 if (l != null && v != null)
3240 MemberSignatures [l.Value] = v.Value;
3242 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3243 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
3245 ReturnType = rt.InnerText;
3246 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
3248 Parameters = new StringList (p.Count);
3249 for (int i = 0; i < p.Count; ++i)
3250 Parameters.Add (p [i].Attributes ["Type"].Value);
3255 public enum MemberFormatterState {
3258 WithinGenericTypeParameters,
3261 public abstract class MemberFormatter {
3263 public virtual string Language {
3267 public virtual string GetName (IMemberReference member)
3269 TypeReference type = member as TypeReference;
3271 return GetTypeName (type);
3272 MethodReference method = member as MethodReference;
3273 if (method != null && method.Name == ".ctor") // method.IsConstructor
3274 return GetConstructorName (method);
3276 return GetMethodName (method);
3277 PropertyReference prop = member as PropertyReference;
3279 return GetPropertyName (prop);
3280 FieldReference field = member as FieldReference;
3282 return GetFieldName (field);
3283 EventReference e = member as EventReference;
3285 return GetEventName (e);
3286 throw new NotSupportedException ("Can't handle: " +
3287 (member == null ? "null" : member.GetType().ToString()));
3290 protected virtual string GetTypeName (TypeReference type)
3293 throw new ArgumentNullException ("type");
3294 return _AppendTypeName (new StringBuilder (type.Name.Length), type).ToString ();
3297 protected virtual char[] ArrayDelimeters {
3298 get {return new char[]{'[', ']'};}
3301 protected virtual MemberFormatterState MemberFormatterState { get; set; }
3303 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type)
3305 if (type is ArrayType) {
3306 TypeSpecification spec = type as TypeSpecification;
3307 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3308 .Append (ArrayDelimeters [0]);
3309 var origState = MemberFormatterState;
3310 MemberFormatterState = MemberFormatterState.WithinArray;
3311 ArrayType array = (ArrayType) type;
3312 int rank = array.Rank;
3314 buf.Append (new string (',', rank-1));
3315 MemberFormatterState = origState;
3316 return buf.Append (ArrayDelimeters [1]);
3318 if (type is ReferenceType) {
3319 return AppendRefTypeName (buf, type);
3321 if (type is PointerType) {
3322 return AppendPointerTypeName (buf, type);
3324 AppendNamespace (buf, type);
3325 if (type is GenericParameter) {
3326 return AppendTypeName (buf, type);
3328 GenericInstanceType genInst = type as GenericInstanceType;
3329 if (type.GenericParameters.Count == 0 &&
3330 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3331 return AppendFullTypeName (buf, type);
3333 return AppendGenericType (buf, type);
3336 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3338 string ns = DocUtils.GetNamespace (type);
3339 if (ns != null && ns.Length > 0)
3340 buf.Append (ns).Append ('.');
3344 protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type)
3346 if (type.DeclaringType != null)
3347 AppendFullTypeName (buf, type.DeclaringType).Append (NestedTypeSeparator);
3348 return AppendTypeName (buf, type);
3351 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3353 return AppendTypeName (buf, type.Name);
3356 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3358 int n = typename.IndexOf ("`");
3360 return buf.Append (typename.Substring (0, n));
3361 return buf.Append (typename);
3364 protected virtual string RefTypeModifier {
3368 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type)
3370 TypeSpecification spec = type as TypeSpecification;
3371 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3372 .Append (RefTypeModifier);
3375 protected virtual string PointerModifier {
3379 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type)
3381 TypeSpecification spec = type as TypeSpecification;
3382 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3383 .Append (PointerModifier);
3386 protected virtual char[] GenericTypeContainer {
3387 get {return new char[]{'<', '>'};}
3390 protected virtual char NestedTypeSeparator {
3394 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3396 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3397 type is GenericInstanceType ? type.GetOriginalType () : type);
3398 List<TypeReference> genArgs = GetGenericArguments (type);
3401 bool insertNested = false;
3402 foreach (var decl in decls) {
3403 TypeReference declDef = decl.Resolve () ?? decl;
3405 buf.Append (NestedTypeSeparator);
3407 insertNested = true;
3408 AppendTypeName (buf, declDef);
3409 int ac = DocUtils.GetGenericArgumentCount (declDef);
3413 buf.Append (GenericTypeContainer [0]);
3414 var origState = MemberFormatterState;
3415 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3416 _AppendTypeName (buf, genArgs [argIdx++]);
3417 for (int i = 1; i < c; ++i)
3418 _AppendTypeName (buf.Append (","), genArgs [argIdx++]);
3419 MemberFormatterState = origState;
3420 buf.Append (GenericTypeContainer [1]);
3426 protected List<TypeReference> GetGenericArguments (TypeReference type)
3428 var args = new List<TypeReference> ();
3429 GenericInstanceType inst = type as GenericInstanceType;
3431 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
3433 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
3437 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3442 protected virtual string GetConstructorName (MethodReference constructor)
3444 return constructor.Name;
3447 protected virtual string GetMethodName (MethodReference method)
3452 protected virtual string GetPropertyName (PropertyReference property)
3454 return property.Name;
3457 protected virtual string GetFieldName (FieldReference field)
3462 protected virtual string GetEventName (EventReference e)
3467 public virtual string GetDeclaration (IMemberReference member)
3470 throw new ArgumentNullException ("member");
3471 TypeDefinition type = member as TypeDefinition;
3473 return GetTypeDeclaration (type);
3474 MethodDefinition method = member as MethodDefinition;
3475 if (method != null && method.IsConstructor)
3476 return GetConstructorDeclaration (method);
3478 return GetMethodDeclaration (method);
3479 PropertyDefinition prop = member as PropertyDefinition;
3481 return GetPropertyDeclaration (prop);
3482 FieldDefinition field = member as FieldDefinition;
3484 return GetFieldDeclaration (field);
3485 EventDefinition e = member as EventDefinition;
3487 return GetEventDeclaration (e);
3488 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3491 protected virtual string GetTypeDeclaration (TypeDefinition type)
3494 throw new ArgumentNullException ("type");
3495 StringBuilder buf = new StringBuilder (type.Name.Length);
3496 _AppendTypeName (buf, type);
3497 AppendGenericTypeConstraints (buf, type);
3498 return buf.ToString ();
3501 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
3503 return GetConstructorName (constructor);
3506 protected virtual string GetMethodDeclaration (MethodDefinition method)
3508 // Special signature for destructors.
3509 if (method.Name == "Finalize" && method.Parameters.Count == 0)
3510 return GetFinalizerName (method);
3512 StringBuilder buf = new StringBuilder ();
3514 AppendVisibility (buf, method);
3515 if (buf.Length == 0 &&
3516 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3519 AppendModifiers (buf, method);
3521 if (buf.Length != 0)
3523 buf.Append (GetTypeName (method.ReturnType)).Append (" ");
3525 AppendMethodName (buf, method);
3526 AppendGenericMethod (buf, method).Append (" ");
3527 AppendParameters (buf, method, method.Parameters);
3528 AppendGenericMethodConstraints (buf, method);
3529 return buf.ToString ();
3532 protected virtual string GetTypeName (MethodReturnType returnType)
3534 return GetName (returnType.ReturnType);
3537 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3539 return buf.Append (method.Name);
3542 protected virtual string GetFinalizerName (MethodDefinition method)
3547 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3552 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3557 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3562 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3567 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3572 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
3574 return GetPropertyName (property);
3577 protected virtual string GetFieldDeclaration (FieldDefinition field)
3579 return GetFieldName (field);
3582 protected virtual string GetEventDeclaration (EventDefinition e)
3584 return GetEventName (e);
3588 class ILFullMemberFormatter : MemberFormatter {
3590 public override string Language {
3591 get {return "ILAsm";}
3594 protected override char NestedTypeSeparator {
3600 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3602 if (GetBuiltinType (type.FullName) != null)
3604 string ns = DocUtils.GetNamespace (type);
3605 if (ns != null && ns.Length > 0) {
3606 if (type.IsValueType)
3607 buf.Append ("valuetype ");
3609 buf.Append ("class ");
3610 buf.Append (ns).Append ('.');
3615 private static string GetBuiltinType (string t)
3618 case "System.Byte": return "unsigned int8";
3619 case "System.SByte": return "int8";
3620 case "System.Int16": return "int16";
3621 case "System.Int32": return "int32";
3622 case "System.Int64": return "int64";
3623 case "System.IntPtr": return "native int";
3625 case "System.UInt16": return "unsigned int16";
3626 case "System.UInt32": return "unsigned int32";
3627 case "System.UInt64": return "unsigned int64";
3628 case "System.UIntPtr": return "native unsigned int";
3630 case "System.Single": return "float32";
3631 case "System.Double": return "float64";
3632 case "System.Boolean": return "bool";
3633 case "System.Char": return "char";
3634 case "System.Void": return "void";
3635 case "System.String": return "string";
3636 case "System.Object": return "object";
3641 protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
3643 return buf.Append (typename);
3646 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3648 var gp = type as GenericParameter;
3649 if (type is GenericParameter)
3650 return AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
3652 string s = GetBuiltinType (type.FullName);
3654 return buf.Append (s);
3655 return base.AppendTypeName (buf, type);
3658 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
3660 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
3661 return buf.Append (type.Owner is TypeReference ? "!" : "!!");
3663 GenericParameterAttributes attrs = type.Attributes;
3664 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
3665 buf.Append ("class ");
3666 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
3667 buf.Append ("struct ");
3668 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
3669 buf.Append (".ctor ");
3670 ConstraintCollection constraints = type.Constraints;
3671 MemberFormatterState = 0;
3672 if (constraints.Count > 0) {
3673 var full = new ILFullMemberFormatter ();
3674 buf.Append ("(").Append (full.GetName (constraints [0]));
3675 for (int i = 1; i < constraints.Count; ++i) {
3676 buf.Append (", ").Append (full.GetName (constraints [i]));
3680 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3682 if ((attrs & GenericParameterAttributes.Covariant) != 0)
3684 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
3689 protected override string GetTypeDeclaration (TypeDefinition type)
3691 string visibility = GetTypeVisibility (type.Attributes);
3692 if (visibility == null)
3695 StringBuilder buf = new StringBuilder ();
3697 buf.Append (".class ");
3699 buf.Append ("nested ");
3700 buf.Append (visibility).Append (" ");
3701 if (type.IsInterface)
3702 buf.Append ("interface ");
3703 if (type.IsSequentialLayout)
3704 buf.Append ("sequential ");
3705 if (type.IsAutoLayout)
3706 buf.Append ("auto ");
3707 if (type.IsAnsiClass)
3708 buf.Append ("ansi ");
3709 if (type.IsAbstract)
3710 buf.Append ("abstract ");
3711 if (type.IsSerializable)
3712 buf.Append ("serializable ");
3714 buf.Append ("sealed ");
3715 if (type.IsBeforeFieldInit)
3716 buf.Append ("beforefieldinit ");
3717 var state = MemberFormatterState;
3718 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3719 buf.Append (GetName (type));
3720 MemberFormatterState = state;
3721 var full = new ILFullMemberFormatter ();
3722 if (type.BaseType != null) {
3723 buf.Append (" extends ");
3724 if (type.BaseType.FullName == "System.Object")
3725 buf.Append ("System.Object");
3727 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
3730 foreach (var name in type.Interfaces.Cast<TypeReference>()
3731 .Select (i => full.GetName (i))
3732 .OrderBy (n => n)) {
3734 buf.Append (" implements ");
3743 return buf.ToString ();
3746 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3748 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3749 type is GenericInstanceType ? type.GetOriginalType () : type);
3751 foreach (var decl in decls) {
3752 TypeReference declDef = decl.Resolve () ?? decl;
3754 buf.Append (NestedTypeSeparator);
3757 AppendTypeName (buf, declDef);
3761 foreach (TypeReference arg in GetGenericArguments (type)) {
3765 _AppendTypeName (buf, arg);
3771 static string GetTypeVisibility (TypeAttributes ta)
3773 switch (ta & TypeAttributes.VisibilityMask) {
3774 case TypeAttributes.Public:
3775 case TypeAttributes.NestedPublic:
3778 case TypeAttributes.NestedFamily:
3779 case TypeAttributes.NestedFamORAssem:
3787 protected override string GetConstructorDeclaration (MethodDefinition constructor)
3789 return GetMethodDeclaration (constructor);
3792 protected override string GetMethodDeclaration (MethodDefinition method)
3794 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
3797 var buf = new StringBuilder ();
3798 buf.Append (".method ");
3799 AppendVisibility (buf, method);
3800 if (method.IsStatic)
3801 buf.Append ("static ");
3802 if (method.IsHideBySig)
3803 buf.Append ("hidebysig ");
3804 if (method.IsPInvokeImpl) {
3805 var info = method.PInvokeInfo;
3806 buf.Append ("pinvokeimpl (\"")
3807 .Append (info.Module.Name)
3808 .Append ("\" as \"")
3809 .Append (info.EntryPoint)
3811 if (info.IsCharSetAuto)
3812 buf.Append (" auto");
3813 if (info.IsCharSetUnicode)
3814 buf.Append (" unicode");
3815 if (info.IsCharSetAnsi)
3816 buf.Append (" ansi");
3817 if (info.IsCallConvCdecl)
3818 buf.Append (" cdecl");
3819 if (info.IsCallConvStdCall)
3820 buf.Append (" stdcall");
3821 if (info.IsCallConvWinapi)
3822 buf.Append (" winapi");
3823 if (info.IsCallConvThiscall)
3824 buf.Append (" thiscall");
3825 if (info.SupportsLastError)
3826 buf.Append (" lasterr");
3829 if (method.IsSpecialName)
3830 buf.Append ("specialname ");
3831 if (method.IsRuntimeSpecialName)
3832 buf.Append ("rtspecialname ");
3833 if (method.IsNewSlot)
3834 buf.Append ("newslot ");
3835 if (method.IsVirtual)
3836 buf.Append ("virtual ");
3837 if (!method.IsStatic)
3838 buf.Append ("instance ");
3839 _AppendTypeName (buf, method.ReturnType.ReturnType);
3841 .Append (method.Name);
3842 if (method.IsGenericMethod ()) {
3843 var state = MemberFormatterState;
3844 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3845 GenericParameterCollection args = method.GenericParameters;
3846 if (args.Count > 0) {
3848 _AppendTypeName (buf, args [0]);
3849 for (int i = 1; i < args.Count; ++i)
3850 _AppendTypeName (buf.Append (", "), args [i]);
3853 MemberFormatterState = state;
3858 for (int i = 0; i < method.Parameters.Count; ++i) {
3862 _AppendTypeName (buf, method.Parameters [i].ParameterType);
3864 buf.Append (method.Parameters [i].Name);
3868 buf.Append (" cil");
3869 if (method.IsRuntime)
3870 buf.Append (" runtime");
3871 if (method.IsManaged)
3872 buf.Append (" managed");
3874 return buf.ToString ();
3877 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3879 if (DocUtils.IsExplicitlyImplemented (method)) {
3880 TypeReference iface;
3881 MethodReference ifaceMethod;
3882 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3883 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3885 .Append (ifaceMethod.Name);
3887 return base.AppendMethodName (buf, method);
3890 protected override string RefTypeModifier {
3894 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3896 if (method.IsPublic)
3897 return buf.Append ("public ");
3898 if (method.IsFamilyAndAssembly)
3899 return buf.Append ("familyandassembly");
3900 if (method.IsFamilyOrAssembly)
3901 return buf.Append ("familyorassembly");
3902 if (method.IsFamily)
3903 return buf.Append ("family");
3907 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3909 string modifiers = String.Empty;
3910 if (method.IsStatic) modifiers += " static";
3911 if (method.IsVirtual && !method.IsAbstract) {
3912 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3913 else modifiers += " override";
3915 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
3916 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
3917 if (method.IsFinal) modifiers += " sealed";
3918 if (modifiers == " virtual sealed") modifiers = "";
3920 return buf.Append (modifiers);
3923 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3925 if (method.IsGenericMethod ()) {
3926 GenericParameterCollection args = method.GenericParameters;
3927 if (args.Count > 0) {
3929 buf.Append (args [0].Name);
3930 for (int i = 1; i < args.Count; ++i)
3931 buf.Append (",").Append (args [i].Name);
3938 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3940 return AppendParameters (buf, method, parameters, '(', ')');
3943 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters, char begin, char end)
3947 if (parameters.Count > 0) {
3948 if (DocUtils.IsExtensionMethod (method))
3949 buf.Append ("this ");
3950 AppendParameter (buf, parameters [0]);
3951 for (int i = 1; i < parameters.Count; ++i) {
3953 AppendParameter (buf, parameters [i]);
3957 return buf.Append (end);
3960 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
3962 if (parameter.ParameterType is ReferenceType) {
3963 if (parameter.IsOut)
3964 buf.Append ("out ");
3966 buf.Append ("ref ");
3968 buf.Append (GetName (parameter.ParameterType)).Append (" ");
3969 return buf.Append (parameter.Name);
3972 protected override string GetPropertyDeclaration (PropertyDefinition property)
3974 MethodDefinition gm = null, sm = null;
3976 string get_visible = null;
3977 if ((gm = property.GetMethod) != null &&
3978 (DocUtils.IsExplicitlyImplemented (gm) ||
3979 (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
3980 get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
3981 string set_visible = null;
3982 if ((sm = property.SetMethod) != null &&
3983 (DocUtils.IsExplicitlyImplemented (sm) ||
3984 (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
3985 set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
3987 if ((set_visible == null) && (get_visible == null))
3991 StringBuilder buf = new StringBuilder ()
3992 .Append (".property ");
3993 if (!(gm ?? sm).IsStatic)
3994 buf.Append ("instance ");
3995 _AppendTypeName (buf, property.PropertyType);
3996 buf.Append (' ').Append (property.Name);
3997 if (!property.HasParameters || property.Parameters.Count == 0)
3998 return buf.ToString ();
4002 foreach (ParameterDefinition p in property.Parameters) {
4006 _AppendTypeName (buf, p.ParameterType);
4010 return buf.ToString ();
4013 protected override string GetFieldDeclaration (FieldDefinition field)
4015 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4016 if (declType.IsEnum && field.Name == "value__")
4017 return null; // This member of enums aren't documented.
4019 StringBuilder buf = new StringBuilder ();
4020 AppendFieldVisibility (buf, field);
4021 if (buf.Length == 0)
4024 buf.Insert (0, ".field ");
4027 buf.Append ("static ");
4028 if (field.IsInitOnly)
4029 buf.Append ("initonly ");
4030 if (field.IsLiteral)
4031 buf.Append ("literal ");
4032 _AppendTypeName (buf, field.FieldType);
4033 buf.Append (' ').Append (field.Name);
4034 AppendFieldValue (buf, field);
4036 return buf.ToString ();
4039 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4042 return buf.Append ("public ");
4043 if (field.IsFamilyAndAssembly)
4044 return buf.Append ("familyandassembly ");
4045 if (field.IsFamilyOrAssembly)
4046 return buf.Append ("familyorassembly ");
4048 return buf.Append ("family ");
4052 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4054 // enums have a value__ field, which we ignore
4055 if (field.DeclaringType.IsGenericType ())
4057 if (field.HasConstant && field.IsLiteral) {
4060 val = field.Constant;
4065 buf.Append (" = ").Append ("null");
4066 else if (val is Enum)
4068 .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4070 .Append (val.ToString ())
4072 else if (val is IFormattable) {
4073 string value = ((IFormattable)val).ToString();
4076 buf.Append ("\"" + value + "\"");
4078 buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4087 protected override string GetEventDeclaration (EventDefinition e)
4089 StringBuilder buf = new StringBuilder ();
4090 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4095 buf.Append (".event ")
4096 .Append (GetName (e.EventType))
4100 return buf.ToString ();
4104 class ILMemberFormatter : ILFullMemberFormatter {
4105 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4111 class CSharpFullMemberFormatter : MemberFormatter {
4113 public override string Language {
4117 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4119 string ns = DocUtils.GetNamespace (type);
4120 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
4121 buf.Append (ns).Append ('.');
4125 private string GetCSharpType (string t)
4128 case "System.Byte": return "byte";
4129 case "System.SByte": return "sbyte";
4130 case "System.Int16": return "short";
4131 case "System.Int32": return "int";
4132 case "System.Int64": return "long";
4134 case "System.UInt16": return "ushort";
4135 case "System.UInt32": return "uint";
4136 case "System.UInt64": return "ulong";
4138 case "System.Single": return "float";
4139 case "System.Double": return "double";
4140 case "System.Decimal": return "decimal";
4141 case "System.Boolean": return "bool";
4142 case "System.Char": return "char";
4143 case "System.Void": return "void";
4144 case "System.String": return "string";
4145 case "System.Object": return "object";
4150 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
4152 if (type is GenericParameter)
4153 return AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
4154 string t = type.FullName;
4155 if (!t.StartsWith ("System.")) {
4156 return base.AppendTypeName (buf, type);
4159 string s = GetCSharpType (t);
4161 return buf.Append (s);
4163 return base.AppendTypeName (buf, type);
4166 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
4168 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
4170 GenericParameterAttributes attrs = type.Attributes;
4171 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
4172 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
4176 buf.Append ("out ");
4180 protected override string GetTypeDeclaration (TypeDefinition type)
4182 string visibility = GetTypeVisibility (type.Attributes);
4183 if (visibility == null)
4186 StringBuilder buf = new StringBuilder ();
4188 buf.Append (visibility);
4191 MemberFormatter full = new CSharpFullMemberFormatter ();
4193 if (DocUtils.IsDelegate (type)) {
4194 buf.Append("delegate ");
4195 MethodDefinition invoke = type.GetMethod ("Invoke");
4196 buf.Append (full.GetName (invoke.ReturnType.ReturnType)).Append (" ");
4197 buf.Append (GetName (type));
4198 AppendParameters (buf, invoke, invoke.Parameters);
4199 AppendGenericTypeConstraints (buf, type);
4202 return buf.ToString();
4205 if (type.IsAbstract && !type.IsInterface)
4206 buf.Append("abstract ");
4207 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
4208 buf.Append("sealed ");
4209 buf.Replace ("abstract sealed", "static");
4211 buf.Append (GetTypeKind (type));
4213 buf.Append (GetCSharpType (type.FullName) == null
4218 TypeReference basetype = type.BaseType;
4219 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
4222 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
4223 .Select (iface => full.GetName (iface))
4227 if (basetype != null || interface_names.Count > 0)
4230 if (basetype != null) {
4231 buf.Append (full.GetName (basetype));
4232 if (interface_names.Count > 0)
4236 for (int i = 0; i < interface_names.Count; i++){
4239 buf.Append (interface_names [i]);
4241 AppendGenericTypeConstraints (buf, type);
4244 return buf.ToString ();
4247 static string GetTypeKind (TypeDefinition t)
4253 if (t.IsClass || t.FullName == "System.Enum")
4257 throw new ArgumentException(t.FullName);
4260 static string GetTypeVisibility (TypeAttributes ta)
4262 switch (ta & TypeAttributes.VisibilityMask) {
4263 case TypeAttributes.Public:
4264 case TypeAttributes.NestedPublic:
4267 case TypeAttributes.NestedFamily:
4268 case TypeAttributes.NestedFamORAssem:
4276 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4278 if (type.GenericParameters.Count == 0)
4280 return AppendConstraints (buf, type.GenericParameters);
4283 private StringBuilder AppendConstraints (StringBuilder buf, GenericParameterCollection genArgs)
4285 foreach (GenericParameter genArg in genArgs) {
4286 GenericParameterAttributes attrs = genArg.Attributes;
4287 ConstraintCollection constraints = genArg.Constraints;
4288 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
4291 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
4292 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
4293 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
4296 if (!isref && !isvt && !isnew && constraints.Count == 0)
4298 buf.Append (" where ").Append (genArg.Name).Append (" : ");
4300 buf.Append ("class");
4304 buf.Append ("struct");
4307 if (constraints.Count > 0 && !isvt) {
4310 buf.Append (GetTypeName (constraints [0]));
4311 for (int i = 1; i < constraints.Count; ++i)
4312 buf.Append (", ").Append (GetTypeName (constraints [i]));
4314 if (isnew && !isvt) {
4317 buf.Append ("new()");
4323 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4325 StringBuilder buf = new StringBuilder ();
4326 AppendVisibility (buf, constructor);
4327 if (buf.Length == 0)
4331 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
4332 AppendParameters (buf, constructor, constructor.Parameters);
4335 return buf.ToString ();
4338 protected override string GetMethodDeclaration (MethodDefinition method)
4340 string decl = base.GetMethodDeclaration (method);
4346 protected override string GetTypeName (MethodReturnType returnType)
4348 return GetTypeName (returnType, () => returnType.ReturnType);
4351 string GetTypeName (ICustomAttributeProvider provider, Func<TypeReference> selector)
4353 string type = GetName (selector ());
4354 if (type == "object" && provider.HasCustomAttributes &&
4355 provider.CustomAttributes.Cast<CustomAttribute>()
4356 .Any (ca => ca.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.DynamicAttribute"))
4361 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4363 if (DocUtils.IsExplicitlyImplemented (method)) {
4364 TypeReference iface;
4365 MethodReference ifaceMethod;
4366 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4367 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4369 .Append (ifaceMethod.Name);
4371 return base.AppendMethodName (buf, method);
4374 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4376 if (method.GenericParameters.Count == 0)
4378 return AppendConstraints (buf, method.GenericParameters);
4381 protected override string RefTypeModifier {
4385 protected override string GetFinalizerName (MethodDefinition method)
4387 return "~" + method.DeclaringType.Name + " ()";
4390 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4394 if (method.IsPublic)
4395 return buf.Append ("public");
4396 if (method.IsFamily || method.IsFamilyOrAssembly)
4397 return buf.Append ("protected");
4401 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4403 string modifiers = String.Empty;
4404 if (method.IsStatic) modifiers += " static";
4405 if (method.IsVirtual && !method.IsAbstract) {
4406 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4407 else modifiers += " override";
4409 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4410 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4411 if (method.IsFinal) modifiers += " sealed";
4412 if (modifiers == " virtual sealed") modifiers = "";
4414 return buf.Append (modifiers);
4417 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4419 if (method.IsGenericMethod ()) {
4420 GenericParameterCollection args = method.GenericParameters;
4421 if (args.Count > 0) {
4423 buf.Append (args [0].Name);
4424 for (int i = 1; i < args.Count; ++i)
4425 buf.Append (",").Append (args [i].Name);
4432 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
4434 return AppendParameters (buf, method, parameters, '(', ')');
4437 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters, char begin, char end)
4441 if (parameters.Count > 0) {
4442 if (DocUtils.IsExtensionMethod (method))
4443 buf.Append ("this ");
4444 AppendParameter (buf, parameters [0]);
4445 for (int i = 1; i < parameters.Count; ++i) {
4447 AppendParameter (buf, parameters [i]);
4451 return buf.Append (end);
4454 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4456 if (parameter.ParameterType is ReferenceType) {
4457 if (parameter.IsOut)
4458 buf.Append ("out ");
4460 buf.Append ("ref ");
4462 buf.Append (GetTypeName (parameter, () => parameter.ParameterType)).Append (" ");
4463 return buf.Append (parameter.Name);
4466 protected override string GetPropertyDeclaration (PropertyDefinition property)
4468 MethodDefinition method;
4470 string get_visible = null;
4471 if ((method = property.GetMethod) != null &&
4472 (DocUtils.IsExplicitlyImplemented (method) ||
4473 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
4474 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
4475 string set_visible = null;
4476 if ((method = property.SetMethod) != null &&
4477 (DocUtils.IsExplicitlyImplemented (method) ||
4478 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
4479 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
4481 if ((set_visible == null) && (get_visible == null))
4485 StringBuilder buf = new StringBuilder ();
4486 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
4487 buf.Append (visibility = get_visible);
4488 else if (set_visible != null && get_visible == null)
4489 buf.Append (visibility = set_visible);
4491 buf.Append (visibility = "public");
4493 // Pick an accessor to use for static/virtual/override/etc. checks.
4494 method = property.SetMethod;
4496 method = property.GetMethod;
4498 string modifiers = String.Empty;
4499 if (method.IsStatic) modifiers += " static";
4500 if (method.IsVirtual && !method.IsAbstract) {
4501 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
4502 modifiers += " virtual";
4504 modifiers += " override";
4506 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
4507 if (method.IsAbstract && !declDef.IsInterface)
4508 modifiers += " abstract";
4510 modifiers += " sealed";
4511 if (modifiers == " virtual sealed")
4513 buf.Append (modifiers).Append (' ');
4515 buf.Append (GetName (property.PropertyType)).Append (' ');
4517 IEnumerable<IMemberReference> defs = property.DeclaringType.GetDefaultMembers ();
4518 string name = property.Name;
4519 foreach (IMemberReference mi in defs) {
4520 if (mi == property) {
4525 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
4527 if (property.Parameters.Count != 0) {
4528 AppendParameters (buf, method, property.Parameters, '[', ']');
4532 if (set_visible != null) {
4533 if (set_visible != visibility)
4534 buf.Append (' ').Append (set_visible);
4535 buf.Append (" set;");
4537 if (get_visible != null) {
4538 if (get_visible != visibility)
4539 buf.Append (' ').Append (get_visible);
4540 buf.Append (" get;");
4544 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
4547 protected override string GetFieldDeclaration (FieldDefinition field)
4549 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4550 if (declType.IsEnum && field.Name == "value__")
4551 return null; // This member of enums aren't documented.
4553 StringBuilder buf = new StringBuilder ();
4554 AppendFieldVisibility (buf, field);
4555 if (buf.Length == 0)
4558 if (declType.IsEnum)
4561 if (field.IsStatic && !field.IsLiteral)
4562 buf.Append (" static");
4563 if (field.IsInitOnly)
4564 buf.Append (" readonly");
4565 if (field.IsLiteral)
4566 buf.Append (" const");
4568 buf.Append (' ').Append (GetName (field.FieldType)).Append (' ');
4569 buf.Append (field.Name);
4570 AppendFieldValue (buf, field);
4573 return buf.ToString ();
4576 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4579 return buf.Append ("public");
4580 if (field.IsFamily || field.IsFamilyOrAssembly)
4581 return buf.Append ("protected");
4585 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4587 // enums have a value__ field, which we ignore
4588 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
4589 field.DeclaringType.IsGenericType ())
4591 if (field.HasConstant && field.IsLiteral) {
4594 val = field.Constant;
4599 buf.Append (" = ").Append ("null");
4600 else if (val is Enum)
4601 buf.Append (" = ").Append (val.ToString ());
4602 else if (val is IFormattable) {
4603 string value = ((IFormattable)val).ToString();
4605 value = "\"" + value + "\"";
4606 buf.Append (" = ").Append (value);
4612 protected override string GetEventDeclaration (EventDefinition e)
4614 StringBuilder buf = new StringBuilder ();
4615 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4619 AppendModifiers (buf, e.AddMethod);
4621 buf.Append (" event ");
4622 buf.Append (GetName (e.EventType)).Append (' ');
4623 buf.Append (e.Name).Append (';');
4625 return buf.ToString ();
4629 class CSharpMemberFormatter : CSharpFullMemberFormatter {
4630 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4636 class DocTypeFullMemberFormatter : MemberFormatter {
4637 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
4639 protected override char NestedTypeSeparator {
4644 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
4645 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4651 class SlashDocMemberFormatter : MemberFormatter {
4653 protected override char[] GenericTypeContainer {
4654 get {return new char[]{'{', '}'};}
4657 private bool AddTypeCount = true;
4659 private TypeReference genDeclType;
4660 private MethodReference genDeclMethod;
4662 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
4664 if (type is GenericParameter) {
4666 if (genDeclType != null) {
4667 GenericParameterCollection genArgs = genDeclType.GenericParameters;
4668 for (int i = 0; i < genArgs.Count; ++i) {
4669 if (genArgs [i].Name == type.Name) {
4670 buf.Append ('`').Append (i);
4675 if (genDeclMethod != null) {
4676 GenericParameterCollection genArgs = null;
4677 if (genDeclMethod.IsGenericMethod ()) {
4678 genArgs = genDeclMethod.GenericParameters;
4679 for (int i = 0; i < genArgs.Count; ++i) {
4680 if (genArgs [i].Name == type.Name) {
4681 buf.Append ("``").Append (i);
4687 if (genDeclType == null && genDeclMethod == null) {
4688 // Probably from within an explicitly implemented interface member,
4689 // where CSC uses parameter names instead of indices (why?), e.g.
4690 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
4691 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
4692 buf.Append (type.Name);
4694 if (buf.Length == l) {
4695 throw new Exception (string.Format (
4696 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
4697 type.Name, genDeclType, genDeclMethod));
4701 base.AppendTypeName (buf, type);
4703 int numArgs = type.GenericParameters.Count;
4704 if (type.DeclaringType != null)
4705 numArgs -= type.GenericParameters.Count;
4707 buf.Append ('`').Append (numArgs);
4714 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
4717 base.AppendGenericType (buf, type);
4719 AppendType (buf, type);
4723 private StringBuilder AppendType (StringBuilder buf, TypeReference type)
4725 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
4726 bool insertNested = false;
4727 int prevParamCount = 0;
4728 foreach (var decl in decls) {
4730 buf.Append (NestedTypeSeparator);
4731 insertNested = true;
4732 base.AppendTypeName (buf, decl);
4733 int argCount = DocUtils.GetGenericArgumentCount (decl);
4734 int numArgs = argCount - prevParamCount;
4735 prevParamCount = argCount;
4737 buf.Append ('`').Append (numArgs);
4742 public override string GetDeclaration (IMemberReference member)
4744 TypeReference r = member as TypeReference;
4746 return "T:" + GetTypeName (r);
4748 return base.GetDeclaration (member);
4751 protected override string GetConstructorName (MethodReference constructor)
4753 return GetMethodDefinitionName (constructor, "#ctor");
4756 protected override string GetMethodName (MethodReference method)
4759 MethodDefinition methodDef = method as MethodDefinition;
4760 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
4763 TypeReference iface;
4764 MethodReference ifaceMethod;
4765 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
4766 AddTypeCount = false;
4767 name = GetTypeName (iface) + "." + ifaceMethod.Name;
4768 AddTypeCount = true;
4770 return GetMethodDefinitionName (method, name);
4773 private string GetMethodDefinitionName (MethodReference method, string name)
4775 StringBuilder buf = new StringBuilder ();
4776 buf.Append (GetTypeName (method.DeclaringType));
4778 buf.Append (name.Replace (".", "#"));
4779 if (method.IsGenericMethod ()) {
4780 GenericParameterCollection genArgs = method.GenericParameters;
4781 if (genArgs.Count > 0)
4782 buf.Append ("``").Append (genArgs.Count);
4784 ParameterDefinitionCollection parameters = method.Parameters;
4786 genDeclType = method.DeclaringType;
4787 genDeclMethod = method;
4788 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
4792 genDeclMethod = null;
4794 return buf.ToString ();
4797 private StringBuilder AppendParameters (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinitionCollection parameters)
4799 if (parameters.Count == 0)
4804 AppendParameter (buf, genArgs, parameters [0]);
4805 for (int i = 1; i < parameters.Count; ++i) {
4807 AppendParameter (buf, genArgs, parameters [i]);
4810 return buf.Append (')');
4813 private StringBuilder AppendParameter (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinition parameter)
4815 AddTypeCount = false;
4816 buf.Append (GetTypeName (parameter.ParameterType));
4817 AddTypeCount = true;
4821 protected override string GetPropertyName (PropertyReference property)
4825 PropertyDefinition propertyDef = property as PropertyDefinition;
4826 MethodDefinition method = null;
4827 if (propertyDef != null)
4828 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
4829 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
4830 name = property.Name;
4832 TypeReference iface;
4833 MethodReference ifaceMethod;
4834 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4835 AddTypeCount = false;
4836 name = string.Join ("#", new string[]{
4837 GetTypeName (iface).Replace (".", "#"),
4838 DocUtils.GetMember (property.Name)
4840 AddTypeCount = true;
4843 StringBuilder buf = new StringBuilder ();
4844 buf.Append (GetName (property.DeclaringType));
4847 ParameterDefinitionCollection parameters = property.Parameters;
4848 if (parameters.Count > 0) {
4849 genDeclType = property.DeclaringType;
4851 GenericParameterCollection genArgs = property.DeclaringType.GenericParameters;
4852 AppendParameter (buf, genArgs, parameters [0]);
4853 for (int i = 1; i < parameters.Count; ++i) {
4855 AppendParameter (buf, genArgs, parameters [i]);
4860 return buf.ToString ();
4863 protected override string GetFieldName (FieldReference field)
4865 return string.Format ("{0}.{1}",
4866 GetName (field.DeclaringType), field.Name);
4869 protected override string GetEventName (EventReference e)
4871 return string.Format ("{0}.{1}",
4872 GetName (e.DeclaringType), e.Name);
4875 protected override string GetTypeDeclaration (TypeDefinition type)
4877 string name = GetName (type);
4883 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4885 string name = GetName (constructor);
4891 protected override string GetMethodDeclaration (MethodDefinition method)
4893 string name = GetName (method);
4896 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4897 genDeclType = method.DeclaringType;
4898 genDeclMethod = method;
4899 name += "~" + GetName (method.ReturnType.ReturnType);
4901 genDeclMethod = null;
4906 protected override string GetPropertyDeclaration (PropertyDefinition property)
4908 string name = GetName (property);
4914 protected override string GetFieldDeclaration (FieldDefinition field)
4916 string name = GetName (field);
4922 protected override string GetEventDeclaration (EventDefinition e)
4924 string name = GetName (e);
4931 class FileNameMemberFormatter : SlashDocMemberFormatter {
4932 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4937 protected override char NestedTypeSeparator {