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.Collections.ObjectModel;
9 using System.Diagnostics;
10 using System.Globalization;
15 using System.Xml.XPath;
20 using MyXmlNodeList = System.Collections.Generic.List<System.Xml.XmlNode>;
21 using StringList = System.Collections.Generic.List<string>;
22 using StringToStringMap = System.Collections.Generic.Dictionary<string, string>;
23 using StringToXmlNodeMap = System.Collections.Generic.Dictionary<string, System.Xml.XmlNode>;
25 namespace Mono.Documentation {
27 class MDocUpdater : MDocCommand
30 List<AssemblyDefinition> assemblies;
31 readonly DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver();
35 bool no_assembly_versions, ignore_missing_types;
36 ExceptionLocations? exceptions;
38 internal int additions = 0, deletions = 0;
40 List<DocumentationImporter> importers = new List<DocumentationImporter> ();
42 DocumentationEnumerator docEnum;
46 static readonly MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
47 static readonly MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
49 static MemberFormatter[] typeFormatters = new MemberFormatter[]{
50 new CSharpMemberFormatter (),
51 new ILMemberFormatter (),
54 static MemberFormatter[] memberFormatters = new MemberFormatter[]{
55 new CSharpFullMemberFormatter (),
56 new ILFullMemberFormatter (),
59 internal static readonly MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
61 MyXmlNodeList extensionMethods = new MyXmlNodeList ();
63 public override void Run (IEnumerable<string> args)
65 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 = AssemblyDefinition.ReadAssembly (name, new ReaderParameters { AssemblyResolver = assemblyResolver });
218 } catch (System.IO.FileNotFoundException) { }
220 if (assembly == null)
221 throw new InvalidOperationException("Assembly " + name + " not found.");
226 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
227 OrderTypeAttributes (element);
228 XmlTextWriter writer = new XmlTextWriter(output);
229 writer.Formatting = Formatting.Indented;
230 writer.Indentation = 2;
231 writer.IndentChar = ' ';
232 element.WriteTo(writer);
236 private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
238 Action<string> creator = file => {
239 using (var writer = OpenWrite (file, mode))
243 MdocFile.UpdateFile (filename, creator);
246 private static void OrderTypeAttributes (XmlElement e)
248 foreach (XmlElement type in e.SelectNodes ("//Type")) {
249 OrderTypeAttributes (type.Attributes);
253 static readonly string[] TypeAttributeOrder = {
254 "Name", "FullName", "FullNameSP", "Maintainer"
257 private static void OrderTypeAttributes (XmlAttributeCollection c)
259 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
260 for (int i = 0; i < c.Count; ++i) {
261 XmlAttribute a = c [i];
262 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
263 if (a.Name == TypeAttributeOrder [j]) {
269 for (int i = attrs.Length-1; i >= 0; --i) {
270 XmlAttribute n = attrs [i];
273 XmlAttribute r = null;
274 for (int j = i+1; j < attrs.Length; ++j) {
275 if (attrs [j] != null) {
283 c.InsertBefore (n, r);
287 private XmlDocument CreateIndexStub()
289 XmlDocument index = new XmlDocument();
291 XmlElement index_root = index.CreateElement("Overview");
292 index.AppendChild(index_root);
294 if (assemblies.Count == 0)
295 throw new Exception ("No assembly");
297 XmlElement index_assemblies = index.CreateElement("Assemblies");
298 index_root.AppendChild(index_assemblies);
300 XmlElement index_remarks = index.CreateElement("Remarks");
301 index_remarks.InnerText = "To be added.";
302 index_root.AppendChild(index_remarks);
304 XmlElement index_copyright = index.CreateElement("Copyright");
305 index_copyright.InnerText = "To be added.";
306 index_root.AppendChild(index_copyright);
308 XmlElement index_types = index.CreateElement("Types");
309 index_root.AppendChild(index_types);
314 private static void WriteNamespaceStub(string ns, string outdir) {
315 XmlDocument index = new XmlDocument();
317 XmlElement index_root = index.CreateElement("Namespace");
318 index.AppendChild(index_root);
320 index_root.SetAttribute("Name", ns);
322 XmlElement index_docs = index.CreateElement("Docs");
323 index_root.AppendChild(index_docs);
325 XmlElement index_summary = index.CreateElement("summary");
326 index_summary.InnerText = "To be added.";
327 index_docs.AppendChild(index_summary);
329 XmlElement index_remarks = index.CreateElement("remarks");
330 index_remarks.InnerText = "To be added.";
331 index_docs.AppendChild(index_remarks);
333 WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
334 writer => WriteXml (index.DocumentElement, writer));
337 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
339 var index = CreateIndexForTypes (dest);
341 var found = new HashSet<string> ();
342 foreach (AssemblyDefinition assembly in assemblies) {
343 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames)) {
344 string relpath = DoUpdateType (type, basepath, dest);
348 found.Add (type.FullName);
353 index.Add (assembly);
361 if (ignore_missing_types)
364 var notFound = from n in typenames where !found.Contains (n) select n;
366 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
369 class IndexForTypes {
375 XmlElement index_types;
376 XmlElement index_assemblies;
378 public IndexForTypes (MDocUpdater app, string indexFile, XmlDocument index)
381 this.indexFile = indexFile;
384 index_types = WriteElement (index.DocumentElement, "Types");
385 index_assemblies = WriteElement (index.DocumentElement, "Assemblies");
388 public void Add (AssemblyDefinition assembly)
390 if (index_assemblies.SelectSingleNode ("Assembly[@Name='" + assembly.Name.Name + "']") != null)
393 app.AddIndexAssembly (assembly, index_assemblies);
396 public void Add (TypeDefinition type)
398 app.AddIndexType (type, index_types);
403 SortIndexEntries (index_types);
404 WriteFile (indexFile, FileMode.Create,
405 writer => WriteXml (index.DocumentElement, writer));
409 IndexForTypes CreateIndexForTypes (string dest)
411 string indexFile = Path.Combine (dest, "index.xml");
412 if (File.Exists (indexFile))
414 return new IndexForTypes (this, indexFile, CreateIndexStub ());
417 public string DoUpdateType (TypeDefinition type, string basepath, string dest)
419 if (type.Namespace == null)
420 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
422 if (!IsPublic (type))
425 // Must get the A+B form of the type name.
426 string typename = GetTypeFileName(type);
428 string reltypefile = DocUtils.PathCombine (DocUtils.GetNamespace (type), typename + ".xml");
429 string typefile = Path.Combine (basepath, reltypefile);
430 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
432 string output = null;
435 } else if (dest == "-") {
438 output = Path.Combine (dest, reltypefile);
443 XmlDocument basefile = new XmlDocument();
445 basefile.Load(typefile);
446 } catch (Exception e) {
447 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
450 DoUpdateType2("Updating", basefile, type, output, false);
453 XmlElement td = StubType(type, output);
457 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
460 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
466 public void DoUpdateNS (string ns, string nspath, string outpath)
468 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
469 AssemblyDefinition assembly = assemblies [0];
471 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
472 XmlDocument basefile = new XmlDocument();
473 string typefile = Path.Combine(nspath, file.Name);
475 basefile.Load(typefile);
476 } catch (Exception e) {
477 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
481 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
482 TypeDefinition type = assembly.GetType(typename);
484 Warning ("Type no longer in assembly: " + typename);
488 seenTypes[type] = seenTypes;
489 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false);
492 // Stub types not in the directory
493 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
494 if (type.Namespace != ns || seenTypes.ContainsKey(type))
497 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"));
498 if (td == null) continue;
502 private static string GetTypeFileName (TypeReference type)
504 return filenameFormatter.GetName (type);
507 public static string GetTypeFileName (string typename)
509 StringBuilder filename = new StringBuilder (typename.Length);
513 for (int i = 0; i < typename.Length; ++i) {
514 char c = typename [i];
523 filename.Append ('`').Append ((numArgs+1).ToString());
538 return filename.ToString ();
541 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
543 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
544 index_assembly.SetAttribute ("Name", assembly.Name.Name);
545 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
547 AssemblyNameDefinition name = assembly.Name;
548 if (name.HasPublicKey) {
549 XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
550 var key = new StringBuilder (name.PublicKey.Length*3 + 2);
552 foreach (byte b in name.PublicKey)
553 key.AppendFormat ("{0,2:x2} ", b);
555 pubkey.InnerText = key.ToString ();
556 index_assembly.AppendChild (pubkey);
559 if (!string.IsNullOrEmpty (name.Culture)) {
560 XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
561 culture.InnerText = name.Culture;
562 index_assembly.AppendChild (culture);
565 MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
566 parent.AppendChild(index_assembly);
569 private void AddIndexType (TypeDefinition type, XmlElement index_types)
571 string typename = GetTypeFileName(type);
573 // Add namespace and type nodes into the index file as needed
574 string ns = DocUtils.GetNamespace (type);
575 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode ("Namespace[@Name='" + ns + "']");
576 if (nsnode == null) {
577 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
578 nsnode.SetAttribute ("Name", ns);
579 index_types.AppendChild (nsnode);
581 string doc_typename = GetDocTypeName (type);
582 XmlElement typenode = (XmlElement) nsnode.SelectSingleNode ("Type[@Name='" + typename + "']");
583 if (typenode == null) {
584 typenode = index_types.OwnerDocument.CreateElement ("Type");
585 typenode.SetAttribute ("Name", typename);
586 nsnode.AppendChild (typenode);
588 if (typename != doc_typename)
589 typenode.SetAttribute("DisplayName", doc_typename);
591 typenode.RemoveAttribute("DisplayName");
593 typenode.SetAttribute ("Kind", GetTypeKind (type));
596 private void DoUpdateAssemblies (string source, string dest)
598 string indexfile = dest + "/index.xml";
600 if (System.IO.File.Exists(indexfile)) {
601 index = new XmlDocument();
602 index.Load(indexfile);
605 ClearElement(index.DocumentElement, "Assembly");
606 ClearElement(index.DocumentElement, "Attributes");
608 index = CreateIndexStub();
611 string defaultTitle = "Untitled";
612 if (assemblies.Count == 1)
613 defaultTitle = assemblies[0].Name.Name;
614 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
616 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
617 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
618 index_assemblies.RemoveAll ();
621 HashSet<string> goodfiles = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
623 foreach (AssemblyDefinition assm in assemblies) {
624 AddIndexAssembly (assm, index_assemblies);
625 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
628 SortIndexEntries (index_types);
630 CleanupFiles (dest, goodfiles);
631 CleanupIndexTypes (index_types, goodfiles);
632 CleanupExtensions (index_types);
634 WriteFile (indexfile, FileMode.Create,
635 writer => WriteXml(index.DocumentElement, writer));
638 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
640 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
642 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
643 string typename = GetTypeFileName(type);
644 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0)
647 string reltypepath = DoUpdateType (type, source, dest);
648 if (reltypepath == null)
651 // Add namespace and type nodes into the index file as needed
652 AddIndexType (type, index_types);
654 // Ensure the namespace index file exists
655 string onsdoc = DocUtils.PathCombine (dest, type.Namespace + ".xml");
656 string nsdoc = DocUtils.PathCombine (dest, "ns-" + type.Namespace + ".xml");
657 if (File.Exists (onsdoc)) {
658 File.Move (onsdoc, nsdoc);
661 if (!File.Exists (nsdoc)) {
662 Console.WriteLine("New Namespace File: " + type.Namespace);
663 WriteNamespaceStub(type.Namespace, dest);
666 goodfiles.Add (reltypepath);
670 private static void SortIndexEntries (XmlElement indexTypes)
672 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
673 XmlNodeComparer c = new AttributeNameComparer ();
674 SortXmlNodes (indexTypes, namespaces, c);
676 for (int i = 0; i < namespaces.Count; ++i)
677 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
680 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
682 MyXmlNodeList l = new MyXmlNodeList (children.Count);
683 for (int i = 0; i < children.Count; ++i)
684 l.Add (children [i]);
686 for (int i = l.Count - 1; i > 0; --i) {
687 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
691 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
693 public abstract int Compare (XmlNode x, XmlNode y);
695 public int Compare (object x, object y)
697 return Compare ((XmlNode) x, (XmlNode) y);
701 class AttributeNameComparer : XmlNodeComparer {
704 public AttributeNameComparer ()
709 public AttributeNameComparer (string attribute)
711 this.attribute = attribute;
714 public override int Compare (XmlNode x, XmlNode y)
716 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
720 class VersionComparer : XmlNodeComparer {
721 public override int Compare (XmlNode x, XmlNode y)
723 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
724 string a = GetVersion (x.InnerText);
725 string b = GetVersion (y.InnerText);
726 return new Version (a).CompareTo (new Version (b));
729 static string GetVersion (string v)
731 int n = v.IndexOf ("x");
734 return v.Substring (0, n-1);
738 private static string GetTypeKind (TypeDefinition type)
741 return "Enumeration";
742 if (type.IsValueType)
744 if (type.IsInterface)
746 if (DocUtils.IsDelegate (type))
748 if (type.IsClass || type.FullName == "System.Enum") // FIXME
750 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
753 private static bool IsPublic (TypeDefinition type)
755 TypeDefinition decl = type;
756 while (decl != null) {
757 if (!(decl.IsPublic || decl.IsNestedPublic ||
758 decl.IsNestedFamily || decl.IsNestedFamily || decl.IsNestedFamilyOrAssembly)) {
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 (!no_assembly_versions && 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 MemberReference 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 (!no_assembly_versions && 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 (MemberReference 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.GetElementType ();
1197 if (origBase.IsGenericType ()) {
1198 ClearElement(basenode, "BaseTypeArguments");
1199 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1200 IList<TypeReference> baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1201 IList<GenericParameter> 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 MemberReference 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 (MemberReference mi)
1367 IEnumerable<string> attrs = Enumerable.Empty<string>();
1369 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1371 attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1373 PropertyDefinition pd = mi as PropertyDefinition;
1375 if (pd.GetMethod != null)
1376 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1377 if (pd.SetMethod != null)
1378 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1381 EventDefinition ed = mi as EventDefinition;
1383 if (ed.AddMethod != null)
1384 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1385 if (ed.RemoveMethod != null)
1386 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1392 IEnumerable<string> GetCustomAttributes (IList<CustomAttribute> attributes, string prefix)
1394 foreach (CustomAttribute attribute in attributes.OrderBy (ca => ca.AttributeType.FullName)) {
1396 TypeDefinition attrType = attribute.AttributeType as TypeDefinition;
1397 if (attrType != null && !IsPublic (attrType))
1399 if (slashdocFormatter.GetName (attribute.AttributeType) == null)
1402 if (Array.IndexOf (IgnorableAttributes, attribute.AttributeType.FullName) >= 0)
1405 StringList fields = new StringList ();
1407 for (int i = 0; i < attribute.ConstructorArguments.Count; ++i) {
1408 CustomAttributeArgument argument = attribute.ConstructorArguments [i];
1409 fields.Add (MakeAttributesValueString (
1414 (from namedArg in attribute.Fields
1415 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value })
1417 (from namedArg in attribute.Properties
1418 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value }))
1419 .OrderBy (v => v.Name);
1420 foreach (var d in namedArgs)
1421 fields.Add (string.Format ("{0}={1}", d.Name,
1422 MakeAttributesValueString (d.Value, d.Type)));
1424 string a2 = String.Join(", ", fields.ToArray ());
1425 if (a2 != "") a2 = "(" + a2 + ")";
1427 string name = attribute.GetDeclaringType();
1428 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1429 yield return prefix + name + a2;
1433 static readonly string[] ValidExtensionMembers = {
1442 static readonly string[] ValidExtensionDocMembers = {
1448 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1450 MethodDefinition me = info.Member as MethodDefinition;
1453 if (info.Parameters.Count < 1)
1455 if (!DocUtils.IsExtensionMethod (me))
1458 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1459 XmlNode member = e.CloneNode (true);
1460 em.AppendChild (member);
1461 RemoveExcept (member, ValidExtensionMembers);
1462 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1463 WriteElementText (member, "MemberType", "ExtensionMethod");
1464 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1465 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1466 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1467 member.AppendChild (link);
1468 AddTargets (em, info);
1470 extensionMethods.Add (em);
1473 private static void RemoveExcept (XmlNode node, string[] except)
1477 MyXmlNodeList remove = null;
1478 foreach (XmlNode n in node.ChildNodes) {
1479 if (Array.BinarySearch (except, n.Name) < 0) {
1481 remove = new MyXmlNodeList ();
1486 foreach (XmlNode n in remove)
1487 node.RemoveChild (n);
1490 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1492 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1493 member.PrependChild (targets);
1494 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1495 AppendElementAttributeText (targets, "Target", "Type",
1496 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1499 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1500 IList<TypeReference> constraints = gp.Constraints;
1501 if (constraints.Count == 0)
1502 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1504 foreach (TypeReference c in constraints)
1505 AppendElementAttributeText(targets, "Target", "Type",
1506 slashdocFormatter.GetDeclaration (c));
1510 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1513 TypeDefinition type = field.DeclaringType.Resolve ();
1514 if (type != null && type.IsEnum) return false;
1516 if (type != null && type.IsGenericType ()) return false;
1517 if (!field.HasConstant)
1519 if (field.IsLiteral) {
1520 object val = field.Constant;
1521 if (val == null) value = "null";
1522 else if (val is Enum) value = val.ToString();
1523 else if (val is IFormattable) {
1524 value = ((IFormattable)val).ToString();
1526 value = "\"" + value + "\"";
1528 if (value != null && value != "")
1534 // XML HELPER FUNCTIONS
1536 internal static XmlElement WriteElement(XmlNode parent, string element) {
1537 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1539 string[] path = element.Split('/');
1540 foreach (string p in path) {
1541 ret = (XmlElement)parent.SelectSingleNode(p);
1544 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1545 ename = ename.Substring(0, ename.IndexOf('['));
1546 ret = parent.OwnerDocument.CreateElement(ename);
1547 parent.AppendChild(ret);
1556 private static void WriteElementText(XmlNode parent, string element, string value) {
1557 XmlElement node = WriteElement(parent, element);
1558 node.InnerText = value;
1561 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1563 XmlElement n = parent.OwnerDocument.CreateElement (element);
1564 parent.AppendChild (n);
1565 n.InnerText = value;
1569 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1571 XmlElement n = parent.OwnerDocument.CreateElement (element);
1572 parent.AppendChild (n);
1573 n.SetAttribute (attribute, value);
1577 internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
1579 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1580 dest.AppendChild (copy);
1584 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1585 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1588 node = WriteElement(parent, element);
1589 node.InnerText = value;
1591 private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
1592 XmlElement node = WriteElement(parent, element);
1593 if (node.GetAttribute(attribute) == value) return;
1594 node.SetAttribute(attribute, value);
1596 internal static void ClearElement(XmlElement parent, string name) {
1597 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1599 parent.RemoveChild(node);
1602 // DOCUMENTATION HELPER FUNCTIONS
1604 private void MakeDocNode (DocsNodeInfo info)
1606 List<GenericParameter> genericParams = info.GenericParameters;
1607 IList<ParameterDefinition> parameters = info.Parameters;
1608 TypeReference returntype = info.ReturnType;
1609 bool returnisreturn = info.ReturnIsReturn;
1610 XmlElement e = info.Node;
1611 bool addremarks = info.AddRemarks;
1613 WriteElementInitialText(e, "summary", "To be added.");
1615 if (parameters != null) {
1616 string[] values = new string [parameters.Count];
1617 for (int i = 0; i < values.Length; ++i)
1618 values [i] = parameters [i].Name;
1619 UpdateParameters (e, "param", values);
1622 if (genericParams != null) {
1623 string[] values = new string [genericParams.Count];
1624 for (int i = 0; i < values.Length; ++i)
1625 values [i] = genericParams [i].Name;
1626 UpdateParameters (e, "typeparam", values);
1629 string retnodename = null;
1630 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
1631 retnodename = returnisreturn ? "returns" : "value";
1632 string retnodename_other = !returnisreturn ? "returns" : "value";
1634 // If it has a returns node instead of a value node, change its name.
1635 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1636 if (retother != null) {
1637 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1638 foreach (XmlNode node in retother)
1639 retnode.AppendChild(node.CloneNode(true));
1640 e.ReplaceChild(retnode, retother);
1642 WriteElementInitialText(e, retnodename, "To be added.");
1645 ClearElement(e, "returns");
1646 ClearElement(e, "value");
1650 WriteElementInitialText(e, "remarks", "To be added.");
1652 if (exceptions.HasValue && info.Member != null &&
1653 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
1654 UpdateExceptions (e, info.Member);
1657 foreach (DocumentationImporter importer in importers)
1658 importer.ImportDocumentation (info);
1660 OrderDocsNodes (e, e.ChildNodes);
1661 NormalizeWhitespace(e);
1664 static readonly string[] DocsNodeOrder = {
1665 "typeparam", "param", "summary", "returns", "value", "remarks",
1668 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
1670 ReorderNodes (docs, children, DocsNodeOrder);
1674 private void UpdateParameters (XmlElement e, string element, string[] values)
1676 if (values != null) {
1677 XmlNode[] paramnodes = new XmlNode[values.Length];
1679 // Some documentation had param nodes with leading spaces.
1680 foreach (XmlElement paramnode in e.SelectNodes(element)){
1681 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
1684 // If a member has only one parameter, we can track changes to
1685 // the name of the parameter easily.
1686 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
1687 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
1690 bool reinsert = false;
1692 // Pick out existing and still-valid param nodes, and
1693 // create nodes for parameters not in the file.
1694 Hashtable seenParams = new Hashtable();
1695 for (int pi = 0; pi < values.Length; pi++) {
1696 string p = values [pi];
1699 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
1700 if (paramnodes[pi] != null) continue;
1702 XmlElement pe = e.OwnerDocument.CreateElement(element);
1703 pe.SetAttribute("name", p);
1704 pe.InnerText = "To be added.";
1705 paramnodes[pi] = pe;
1709 // Remove parameters that no longer exist and check all params are in the right order.
1711 MyXmlNodeList todelete = new MyXmlNodeList ();
1712 foreach (XmlElement paramnode in e.SelectNodes(element)) {
1713 string name = paramnode.GetAttribute("name");
1714 if (!seenParams.ContainsKey(name)) {
1715 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1716 Warning ("The following param node can only be deleted if the --delete option is given: ");
1717 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
1719 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
1720 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1724 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
1725 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1726 e.ParentNode.Attributes ["MemberName"].Value,
1729 Warning ("\tValue={0}", paramnode.OuterXml);
1731 todelete.Add (paramnode);
1736 if ((int)seenParams[name] != idx)
1742 foreach (XmlNode n in todelete) {
1743 n.ParentNode.RemoveChild (n);
1746 // Re-insert the parameter nodes at the top of the doc section.
1748 for (int pi = values.Length-1; pi >= 0; pi--)
1749 e.PrependChild(paramnodes[pi]);
1751 // Clear all existing param nodes
1752 foreach (XmlNode paramnode in e.SelectNodes(element)) {
1753 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1754 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
1755 Console.WriteLine(paramnode.OuterXml);
1757 paramnode.ParentNode.RemoveChild(paramnode);
1763 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
1765 string existingName = pe.GetAttribute ("name");
1766 pe.SetAttribute ("name", newName);
1767 if (existingName == newName)
1769 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
1770 if (paramref.GetAttribute ("name").Trim () == existingName)
1771 paramref.SetAttribute ("name", newName);
1774 class CrefComparer : XmlNodeComparer {
1776 public CrefComparer ()
1780 public override int Compare (XmlNode x, XmlNode y)
1782 string xType = x.Attributes ["cref"].Value;
1783 string yType = y.Attributes ["cref"].Value;
1784 string xNamespace = GetNamespace (xType);
1785 string yNamespace = GetNamespace (yType);
1787 int c = xNamespace.CompareTo (yNamespace);
1790 return xType.CompareTo (yType);
1793 static string GetNamespace (string type)
1795 int n = type.LastIndexOf ('.');
1797 return type.Substring (0, n);
1798 return string.Empty;
1802 private void UpdateExceptions (XmlNode docs, MemberReference member)
1804 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
1805 string cref = slashdocFormatter.GetDeclaration (source.Exception);
1806 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
1809 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
1810 e.SetAttribute ("cref", cref);
1811 e.InnerXml = "To be added; from: <see cref=\"" +
1812 string.Join ("\" />, <see cref=\"",
1813 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
1816 docs.AppendChild (e);
1818 SortXmlNodes (docs, docs.SelectNodes ("exception"),
1819 new CrefComparer ());
1822 private static void NormalizeWhitespace(XmlElement e) {
1823 // Remove all text and whitespace nodes from the element so it
1824 // is outputted with nice indentation and no blank lines.
1825 ArrayList deleteNodes = new ArrayList();
1826 foreach (XmlNode n in e)
1827 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
1829 foreach (XmlNode n in deleteNodes)
1830 n.ParentNode.RemoveChild(n);
1833 private static bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
1835 TypeDefinition type = member as TypeDefinition;
1837 type = member.DeclaringType as TypeDefinition;
1838 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
1841 private static string GetAssemblyVersion (AssemblyDefinition assembly)
1843 return assembly.Name.Version.ToString();
1846 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
1848 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
1850 e = root.OwnerDocument.CreateElement("AssemblyInfo");
1851 root.AppendChild(e);
1853 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
1854 .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
1856 // matches.Count > 0 && add: ignore -- already present
1857 if (matches.Count > 0 && !add) {
1858 foreach (XmlNode c in matches)
1861 else if (matches.Count == 0 && add) {
1862 foreach (string sv in assemblyVersions) {
1863 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
1868 // matches.Count == 0 && !add: ignore -- already not present
1870 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
1871 SortXmlNodes (e, avs, new VersionComparer ());
1873 return avs.Count != 0;
1876 // FIXME: get TypeReferences instead of string comparison?
1877 private static string[] IgnorableAttributes = {
1878 // Security related attributes
1879 "System.Reflection.AssemblyKeyFileAttribute",
1880 "System.Reflection.AssemblyDelaySignAttribute",
1881 // Present in @RefType
1882 "System.Runtime.InteropServices.OutAttribute",
1883 // For naming the indexer to use when not using indexers
1884 "System.Reflection.DefaultMemberAttribute",
1885 // for decimal constants
1886 "System.Runtime.CompilerServices.DecimalConstantAttribute",
1887 // compiler generated code
1888 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
1889 // more compiler generated code, e.g. iterator methods
1890 "System.Diagnostics.DebuggerHiddenAttribute",
1891 "System.Runtime.CompilerServices.FixedBufferAttribute",
1892 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
1893 // extension methods
1894 "System.Runtime.CompilerServices.ExtensionAttribute",
1895 // Used to differentiate 'object' from C#4 'dynamic'
1896 "System.Runtime.CompilerServices.DynamicAttribute",
1899 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes)
1901 if (!attributes.Any ()) {
1902 ClearElement (root, "Attributes");
1906 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
1910 e = root.OwnerDocument.CreateElement("Attributes");
1912 foreach (string attribute in attributes) {
1913 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
1916 WriteElementText(ae, "AttributeName", attribute);
1919 if (e.ParentNode == null)
1920 root.AppendChild(e);
1922 NormalizeWhitespace(e);
1925 public static string MakeAttributesValueString (object v, TypeReference valueType)
1929 if (valueType.FullName == "System.Type")
1930 return "typeof(" + v.ToString () + ")";
1931 if (valueType.FullName == "System.String")
1932 return "\"" + v.ToString () + "\"";
1933 if (valueType.FullName == "System.Char")
1934 return "'" + v.ToString () + "'";
1936 return (bool)v ? "true" : "false";
1937 TypeDefinition valueDef = valueType.Resolve ();
1938 if (valueDef == null || !valueDef.IsEnum)
1939 return v.ToString ();
1940 string typename = GetDocTypeFullName (valueType);
1941 var values = GetEnumerationValues (valueDef);
1942 long c = ToInt64 (v);
1943 if (values.ContainsKey (c))
1944 return typename + "." + values [c];
1945 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
1946 return string.Join (" | ",
1947 (from i in values.Keys
1949 select typename + "." + values [i])
1950 .DefaultIfEmpty (v.ToString ()).ToArray ());
1952 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
1955 private static Dictionary<long, string> GetEnumerationValues (TypeDefinition type)
1957 var values = new Dictionary<long, string> ();
1959 (from f in type.Fields
1960 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
1962 values [ToInt64 (f.Constant)] = f.Name;
1967 static long ToInt64 (object value)
1970 return (long) (ulong) value;
1971 return Convert.ToInt64 (value);
1974 private void MakeParameters (XmlElement root, IList<ParameterDefinition> parameters)
1976 XmlElement e = WriteElement(root, "Parameters");
1978 foreach (ParameterDefinition p in parameters) {
1979 XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
1981 pe.SetAttribute("Name", p.Name);
1982 pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
1983 if (p.ParameterType is ByReferenceType) {
1984 if (p.IsOut) pe.SetAttribute("RefType", "out");
1985 else pe.SetAttribute("RefType", "ref");
1987 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
1991 private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams)
1993 if (typeParams == null || typeParams.Count == 0) {
1994 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
1996 root.RemoveChild (f);
1999 XmlElement e = WriteElement(root, "TypeParameters");
2001 foreach (GenericParameter t in typeParams) {
2002 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2004 pe.SetAttribute("Name", t.Name);
2005 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""));
2006 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2007 IList<TypeReference> constraints = t.Constraints;
2008 GenericParameterAttributes attrs = t.Attributes;
2009 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2017 ce = root.OwnerDocument.CreateElement ("Constraints");
2019 pe.AppendChild (ce);
2020 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2021 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2022 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2023 AppendElementText (ce, "ParameterAttribute", "Covariant");
2024 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2025 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2026 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2027 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2028 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2029 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2030 foreach (TypeReference c in constraints) {
2031 TypeDefinition cd = c.Resolve ();
2032 AppendElementText (ce,
2033 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2034 GetDocTypeFullName (c));
2039 private void MakeParameters (XmlElement root, MemberReference mi)
2041 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2042 MakeParameters (root, ((MethodDefinition)mi).Parameters);
2043 else if (mi is MethodDefinition) {
2044 MethodDefinition mb = (MethodDefinition) mi;
2045 IList<ParameterDefinition> parameters = mb.Parameters;
2046 MakeParameters(root, parameters);
2047 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2048 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2049 p.SetAttribute ("RefType", "this");
2052 else if (mi is PropertyDefinition) {
2053 IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
2054 if (parameters.Count > 0)
2055 MakeParameters(root, parameters);
2059 else if (mi is FieldDefinition) return;
2060 else if (mi is EventDefinition) return;
2061 else throw new ArgumentException();
2064 internal static string GetDocParameterType (TypeReference type)
2066 return GetDocTypeFullName (type).Replace ("@", "&");
2069 private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes)
2071 XmlElement e = WriteElement(root, "ReturnValue");
2073 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2074 if (attributes != null)
2075 MakeAttributes(e, GetCustomAttributes (attributes, ""));
2078 private void MakeReturnValue (XmlElement root, MemberReference mi)
2080 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2082 else if (mi is MethodDefinition)
2083 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes);
2084 else if (mi is PropertyDefinition)
2085 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
2086 else if (mi is FieldDefinition)
2087 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
2088 else if (mi is EventDefinition)
2089 MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
2091 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2094 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2096 MemberReference mi = info.Member;
2097 if (mi is TypeDefinition) return null;
2099 string sigs = memberFormatters [0].GetDeclaration (mi);
2100 if (sigs == null) return null; // not publicly visible
2102 // no documentation for property/event accessors. Is there a better way of doing this?
2103 if (mi.Name.StartsWith("get_")) return null;
2104 if (mi.Name.StartsWith("set_")) return null;
2105 if (mi.Name.StartsWith("add_")) return null;
2106 if (mi.Name.StartsWith("remove_")) return null;
2107 if (mi.Name.StartsWith("raise_")) return null;
2109 XmlElement me = doc.CreateElement("Member");
2110 me.SetAttribute("MemberName", GetMemberName (mi));
2114 if (exceptions.HasValue &&
2115 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2116 UpdateExceptions (info.Node, info.Member);
2118 if (since != null) {
2119 XmlNode docs = me.SelectSingleNode("Docs");
2120 docs.AppendChild (CreateSinceNode (doc));
2126 internal static string GetMemberName (MemberReference mi)
2128 MethodDefinition mb = mi as MethodDefinition;
2130 PropertyDefinition pi = mi as PropertyDefinition;
2133 return DocUtils.GetPropertyName (pi);
2135 StringBuilder sb = new StringBuilder (mi.Name.Length);
2136 if (!DocUtils.IsExplicitlyImplemented (mb))
2137 sb.Append (mi.Name);
2139 TypeReference iface;
2140 MethodReference ifaceMethod;
2141 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2142 sb.Append (GetDocTypeFullName (iface));
2144 sb.Append (ifaceMethod.Name);
2146 if (mb.IsGenericMethod ()) {
2147 IList<GenericParameter> typeParams = mb.GenericParameters;
2148 if (typeParams.Count > 0) {
2150 sb.Append (typeParams [0].Name);
2151 for (int i = 1; i < typeParams.Count; ++i)
2152 sb.Append (",").Append (typeParams [i].Name);
2156 return sb.ToString ();
2159 /// SIGNATURE GENERATION FUNCTIONS
2160 internal static bool IsPrivate (MemberReference mi)
2162 return memberFormatters [0].GetDeclaration (mi) == null;
2165 internal static string GetMemberType (MemberReference mi)
2167 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2168 return "Constructor";
2169 if (mi is MethodDefinition)
2171 if (mi is PropertyDefinition)
2173 if (mi is FieldDefinition)
2175 if (mi is EventDefinition)
2177 throw new ArgumentException();
2180 private static string GetDocTypeName (TypeReference type)
2182 return docTypeFormatter.GetName (type);
2185 internal static string GetDocTypeFullName (TypeReference type)
2187 return DocTypeFullMemberFormatter.Default.GetName (type);
2190 internal static string GetXPathForMember (DocumentationMember member)
2192 StringBuilder xpath = new StringBuilder ();
2193 xpath.Append ("//Members/Member[@MemberName=\"")
2194 .Append (member.MemberName)
2196 if (member.Parameters != null && member.Parameters.Count > 0) {
2197 xpath.Append ("/Parameters[count(Parameter) = ")
2198 .Append (member.Parameters.Count);
2199 for (int i = 0; i < member.Parameters.Count; ++i) {
2200 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2201 xpath.Append (member.Parameters [i]);
2202 xpath.Append ("\"");
2204 xpath.Append ("]/..");
2206 return xpath.ToString ();
2209 public static string GetXPathForMember (XPathNavigator member)
2211 StringBuilder xpath = new StringBuilder ();
2212 xpath.Append ("//Type[@FullName=\"")
2213 .Append (member.SelectSingleNode ("../../@FullName").Value)
2215 xpath.Append ("Members/Member[@MemberName=\"")
2216 .Append (member.SelectSingleNode ("@MemberName").Value)
2218 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2219 if (parameters.Count > 0) {
2220 xpath.Append ("/Parameters[count(Parameter) = ")
2221 .Append (parameters.Count);
2223 while (parameters.MoveNext ()) {
2225 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2226 xpath.Append (parameters.Current.Value);
2227 xpath.Append ("\"");
2229 xpath.Append ("]/..");
2231 return xpath.ToString ();
2234 public static string GetXPathForMember (MemberReference member)
2236 StringBuilder xpath = new StringBuilder ();
2237 xpath.Append ("//Type[@FullName=\"")
2238 .Append (member.DeclaringType.FullName)
2240 xpath.Append ("Members/Member[@MemberName=\"")
2241 .Append (GetMemberName (member))
2244 IList<ParameterDefinition> parameters = null;
2245 if (member is MethodDefinition)
2246 parameters = ((MethodDefinition) member).Parameters;
2247 else if (member is PropertyDefinition) {
2248 parameters = ((PropertyDefinition) member).Parameters;
2250 if (parameters != null && parameters.Count > 0) {
2251 xpath.Append ("/Parameters[count(Parameter) = ")
2252 .Append (parameters.Count);
2253 for (int i = 0; i < parameters.Count; ++i) {
2254 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2255 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2256 xpath.Append ("\"");
2258 xpath.Append ("]/..");
2260 return xpath.ToString ();
2264 static class CecilExtensions {
2265 public static string GetDeclaringType(this CustomAttribute attribute)
2267 return attribute.Constructor.DeclaringType.FullName;
2270 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
2272 foreach (var c in type.Methods.Where (m => m.IsConstructor))
2273 yield return (MemberReference) c;
2274 foreach (var e in type.Events)
2275 yield return (MemberReference) e;
2276 foreach (var f in type.Fields)
2277 yield return (MemberReference) f;
2278 foreach (var m in type.Methods.Where (m => !m.IsConstructor))
2279 yield return (MemberReference) m;
2280 foreach (var t in type.NestedTypes)
2281 yield return (MemberReference) t;
2282 foreach (var p in type.Properties)
2283 yield return (MemberReference) p;
2286 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
2288 return GetMembers (type).Where (m => m.Name == member);
2291 public static MemberReference GetMember (this TypeDefinition type, string member)
2293 return GetMembers (type, member).EnsureZeroOrOne ();
2296 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2298 if (source.Count () > 1)
2299 throw new InvalidOperationException ("too many matches");
2300 return source.FirstOrDefault ();
2303 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2306 .Where (m => m.Name == method)
2307 .EnsureZeroOrOne ();
2310 public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
2312 TypeDefinition def = type as TypeDefinition;
2314 return new MemberReference [0];
2315 CustomAttribute defMemberAttr = def.CustomAttributes
2316 .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
2317 if (defMemberAttr == null)
2318 return new MemberReference [0];
2319 string name = (string) defMemberAttr.ConstructorArguments [0].Value;
2320 return def.Properties
2321 .Where (p => p.Name == name)
2322 .Select (p => (MemberReference) p);
2325 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2327 return assembly.Modules.SelectMany (md => md.GetAllTypes ());
2330 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2332 return GetTypes (assembly)
2333 .Where (td => td.FullName == type)
2334 .EnsureZeroOrOne ();
2337 public static bool IsGenericType (this TypeReference type)
2339 return type.GenericParameters.Count > 0;
2342 public static bool IsGenericMethod (this MethodReference method)
2344 return method.GenericParameters.Count > 0;
2347 public static MemberReference Resolve (this MemberReference member)
2349 FieldReference fr = member as FieldReference;
2351 return fr.Resolve ();
2352 MethodReference mr = member as MethodReference;
2354 return mr.Resolve ();
2355 TypeReference tr = member as TypeReference;
2357 return tr.Resolve ();
2358 PropertyReference pr = member as PropertyReference;
2361 EventReference er = member as EventReference;
2364 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2367 public static TypeReference GetUnderlyingType (this TypeDefinition type)
2371 return type.Fields.First (f => f.Name == "value__").FieldType;
2374 public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
2376 return self.Types.SelectMany (t => t.GetAllTypes ());
2379 static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
2383 if (!self.HasNestedTypes)
2386 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
2391 static class DocUtils {
2392 public static bool IsExplicitlyImplemented (MethodDefinition method)
2394 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2397 public static string GetTypeDotMember (string name)
2399 int startType, startMethod;
2400 startType = startMethod = -1;
2401 for (int i = 0; i < name.Length; ++i) {
2402 if (name [i] == '.') {
2403 startType = startMethod;
2407 return name.Substring (startType+1);
2410 public static string GetMember (string name)
2412 int i = name.LastIndexOf ('.');
2415 return name.Substring (i+1);
2418 public static void GetInfoForExplicitlyImplementedMethod (
2419 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2423 if (method.Overrides.Count != 1)
2424 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2425 iface = method.Overrides [0].DeclaringType;
2426 ifaceMethod = method.Overrides [0];
2429 public static string GetPropertyName (PropertyDefinition pi)
2431 // Issue: (g)mcs-generated assemblies that explicitly implement
2432 // properties don't specify the full namespace, just the
2433 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2434 MethodDefinition method = pi.GetMethod;
2436 method = pi.SetMethod;
2437 if (!IsExplicitlyImplemented (method))
2440 // Need to determine appropriate namespace for this member.
2441 TypeReference iface;
2442 MethodReference ifaceMethod;
2443 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2444 return string.Join (".", new string[]{
2445 DocTypeFullMemberFormatter.Default.GetName (iface),
2446 GetMember (pi.Name)});
2449 public static string GetNamespace (TypeReference type)
2451 if (type.GetElementType ().IsNested)
2452 type = type.GetElementType ();
2453 while (type != null && type.IsNested)
2454 type = type.DeclaringType;
2456 return string.Empty;
2457 return type.Namespace;
2460 public static string PathCombine (string dir, string path)
2466 return Path.Combine (dir, path);
2469 public static bool IsExtensionMethod (MethodDefinition method)
2472 method.CustomAttributes
2473 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2474 && method.DeclaringType.CustomAttributes
2475 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
2478 public static bool IsDelegate (TypeDefinition type)
2480 TypeReference baseRef = type.BaseType;
2481 if (baseRef == null)
2483 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2484 baseRef.FullName == "System.MulticastDelegate";
2487 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2489 List<TypeReference> decls = new List<TypeReference> ();
2491 while (type.DeclaringType != null) {
2492 decls.Add (type.DeclaringType);
2493 type = type.DeclaringType;
2499 public static int GetGenericArgumentCount (TypeReference type)
2501 GenericInstanceType inst = type as GenericInstanceType;
2503 ? inst.GenericArguments.Count
2504 : type.GenericParameters.Count;
2507 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
2509 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
2510 List<TypeReference> userInterfaces = new List<TypeReference> ();
2511 foreach (TypeReference iface in type.Interfaces) {
2512 TypeReference lookup = iface.Resolve () ?? iface;
2513 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
2514 userInterfaces.Add (iface);
2516 return userInterfaces;
2519 private static string GetQualifiedTypeName (TypeReference type)
2521 return "[" + type.Scope.Name + "]" + type.FullName;
2524 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
2526 HashSet<string> inheritedInterfaces = new HashSet<string> ();
2527 Action<TypeDefinition> a = null;
2529 if (t == null) return;
2530 foreach (TypeReference r in t.Interfaces) {
2531 inheritedInterfaces.Add (GetQualifiedTypeName (r));
2535 TypeReference baseRef = type.BaseType;
2536 while (baseRef != null) {
2537 TypeDefinition baseDef = baseRef.Resolve ();
2538 if (baseDef != null) {
2540 baseRef = baseDef.BaseType;
2545 foreach (TypeReference r in type.Interfaces)
2547 return inheritedInterfaces;
2551 class DocsNodeInfo {
2552 public DocsNodeInfo (XmlElement node)
2557 public DocsNodeInfo (XmlElement node, TypeDefinition type)
2563 public DocsNodeInfo (XmlElement node, MemberReference member)
2566 SetMemberInfo (member);
2569 void SetType (TypeDefinition type)
2572 throw new ArgumentNullException ("type");
2574 GenericParameters = new List<GenericParameter> (type.GenericParameters);
2575 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
2576 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
2577 for (int i = 0; i < declTypes.Count - 1; ++i) {
2578 int remove = System.Math.Min (maxGenArgs,
2579 DocUtils.GetGenericArgumentCount (declTypes [i]));
2580 maxGenArgs -= remove;
2581 while (remove-- > 0)
2582 GenericParameters.RemoveAt (0);
2584 if (DocUtils.IsDelegate (type)) {
2585 Parameters = type.GetMethod("Invoke").Parameters;
2586 ReturnType = type.GetMethod("Invoke").ReturnType;
2587 ReturnIsReturn = true;
2591 void SetMemberInfo (MemberReference member)
2594 throw new ArgumentNullException ("member");
2595 ReturnIsReturn = true;
2599 if (member is MethodReference ) {
2600 MethodReference mr = (MethodReference) member;
2601 Parameters = mr.Parameters;
2602 if (mr.IsGenericMethod ()) {
2603 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
2606 else if (member is PropertyDefinition) {
2607 Parameters = ((PropertyDefinition) member).Parameters;
2610 if (member is MethodDefinition) {
2611 ReturnType = ((MethodDefinition) member).ReturnType;
2612 } else if (member is PropertyDefinition) {
2613 ReturnType = ((PropertyDefinition) member).PropertyType;
2614 ReturnIsReturn = false;
2617 // no remarks section for enum members
2618 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
2622 public TypeReference ReturnType;
2623 public List<GenericParameter> GenericParameters;
2624 public IList<ParameterDefinition> Parameters;
2625 public bool ReturnIsReturn;
2626 public XmlElement Node;
2627 public bool AddRemarks = true;
2628 public MemberReference Member;
2629 public TypeDefinition Type;
2632 class DocumentationEnumerator {
2634 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2636 return GetDocumentationTypes (assembly, forTypes, null);
2639 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2641 foreach (TypeDefinition type in assembly.GetTypes()) {
2642 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
2644 if (seen != null && seen.Contains (type.FullName))
2647 foreach (TypeDefinition nested in type.NestedTypes)
2648 yield return nested;
2652 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2654 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
2655 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
2656 oldmember.RemoveAttribute ("__monodocer-seen__");
2659 MemberReference m = GetMember (type, new DocumentationMember (oldmember));
2661 yield return new DocsNodeInfo (oldmember);
2664 yield return new DocsNodeInfo (oldmember, m);
2669 protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
2671 string membertype = member.MemberType;
2673 string returntype = member.ReturnType;
2675 string docName = member.MemberName;
2676 string[] docTypeParams = GetTypeParameters (docName);
2678 // Loop through all members in this type with the same name
2679 foreach (MemberReference mi in GetReflectionMembers (type, docName)) {
2680 if (mi is TypeDefinition) continue;
2681 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
2683 if (MDocUpdater.IsPrivate (mi))
2686 IList<ParameterDefinition> pis = null;
2687 string[] typeParams = null;
2688 if (mi is MethodDefinition) {
2689 MethodDefinition mb = (MethodDefinition) mi;
2690 pis = mb.Parameters;
2691 if (docTypeParams != null && mb.IsGenericMethod ()) {
2692 IList<GenericParameter> args = mb.GenericParameters;
2693 if (args.Count == docTypeParams.Length) {
2694 typeParams = args.Select (p => p.Name).ToArray ();
2698 else if (mi is PropertyDefinition)
2699 pis = ((PropertyDefinition)mi).Parameters;
2701 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
2702 int pcount = pis == null ? 0 : pis.Count;
2703 if (mcount != pcount)
2706 MethodDefinition mDef = mi as MethodDefinition;
2707 if (mDef != null && !mDef.IsConstructor) {
2708 // Casting operators can overload based on return type.
2709 if (returntype != GetReplacedString (
2710 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
2711 typeParams, docTypeParams)) {
2719 for (int i = 0; i < pis.Count; i++) {
2720 string paramType = GetReplacedString (
2721 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
2722 typeParams, docTypeParams);
2723 if (paramType != (string) member.Parameters [i]) {
2728 if (!good) continue;
2736 static string[] GetTypeParameters (string docName)
2738 if (docName [docName.Length-1] != '>')
2740 StringList types = new StringList ();
2741 int endToken = docName.Length-2;
2742 int i = docName.Length-2;
2744 if (docName [i] == ',' || docName [i] == '<') {
2745 types.Add (docName.Substring (i + 1, endToken - i));
2748 if (docName [i] == '<')
2753 return types.ToArray ();
2756 protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
2758 // need to worry about 4 forms of //@MemberName values:
2759 // 1. "Normal" (non-generic) member names: GetEnumerator
2761 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
2762 // - try as-is, and try type.member (due to "kludge" for property
2764 // 3. "Normal" Generic member names: Sort<T> (CSC)
2765 // - need to remove generic parameters --> "Sort"
2766 // 4. Explicitly-implemented interface members for generic interfaces:
2767 // -- System.Collections.Generic.IEnumerable<T>.Current
2768 // - Try as-is, and try type.member, *keeping* the generic parameters.
2769 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
2770 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
2771 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
2772 // this as (1) or (2).
2773 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
2775 foreach (MemberReference mi in type.GetMembers (docName))
2777 if (CountChars (docName, '.') > 0)
2778 // might be a property; try only type.member instead of
2779 // namespace.type.member.
2780 foreach (MemberReference mi in
2781 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
2788 int startLt, startType, startMethod;
2789 startLt = startType = startMethod = -1;
2790 for (int i = 0; i < docName.Length; ++i) {
2791 switch (docName [i]) {
2800 if (numLt == 0 && (i + 1) < docName.Length)
2801 // there's another character in docName, so this <...> sequence is
2802 // probably part of a generic type -- case 4.
2806 startType = startMethod;
2812 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
2814 foreach (MemberReference mi in type.GetMembers (refName))
2818 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
2821 // If we _still_ haven't found it, we've hit another generic naming issue:
2822 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
2823 // explicitly-implemented METHOD names (not properties), e.g.
2824 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
2825 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
2826 // which the XML docs will contain.
2828 // Alas, we can't derive the Mono name from docName, so we need to iterate
2829 // over all member names, convert them into CSC format, and compare... :-(
2832 foreach (MemberReference mi in type.GetMembers ()) {
2833 if (MDocUpdater.GetMemberName (mi) == docName)
2838 static string GetReplacedString (string typeName, string[] from, string[] to)
2842 for (int i = 0; i < from.Length; ++i)
2843 typeName = typeName.Replace (from [i], to [i]);
2847 private static int CountChars (string s, char c)
2850 for (int i = 0; i < s.Length; ++i) {
2858 class EcmaDocumentationEnumerator : DocumentationEnumerator {
2863 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
2866 this.ecmadocs = ecmaDocs;
2869 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2871 HashSet<string> seen = new HashSet<string> ();
2872 return GetDocumentationTypes (assembly, forTypes, seen)
2873 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
2876 new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2879 while (ecmadocs.Read ()) {
2880 switch (ecmadocs.Name) {
2882 if (typeDepth == -1)
2883 typeDepth = ecmadocs.Depth;
2884 if (ecmadocs.NodeType != XmlNodeType.Element)
2886 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
2888 string typename = ecmadocs.GetAttribute ("FullName");
2889 string typename2 = MDocUpdater.GetTypeFileName (typename);
2890 if (forTypes != null &&
2891 forTypes.BinarySearch (typename) < 0 &&
2892 typename != typename2 &&
2893 forTypes.BinarySearch (typename2) < 0)
2896 if ((t = assembly.GetType (typename)) == null &&
2897 (t = assembly.GetType (typename2)) == null)
2899 seen.Add (typename);
2900 if (typename != typename2)
2901 seen.Add (typename2);
2902 Console.WriteLine (" Import: {0}", t.FullName);
2903 if (ecmadocs.Name != "Docs") {
2904 int depth = ecmadocs.Depth;
2905 while (ecmadocs.Read ()) {
2906 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
2910 if (!ecmadocs.IsStartElement ("Docs"))
2911 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
2921 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2923 return GetMembers (basefile, type)
2924 .Concat (base.GetDocumentationMembers (basefile, type));
2927 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
2929 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
2932 if (ecmadocs.IsEmptyElement)
2935 int membersDepth = ecmadocs.Depth;
2937 while (go && ecmadocs.Read ()) {
2938 switch (ecmadocs.Name) {
2940 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
2942 DocumentationMember dm = new DocumentationMember (ecmadocs);
2943 string xp = MDocUpdater.GetXPathForMember (dm);
2944 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
2946 if (oldmember == null) {
2947 m = GetMember (type, dm);
2949 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2950 type.FullName, dm.MemberSignatures ["C#"]);
2951 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
2954 // oldmember lookup may have failed due to type parameter renames.
2956 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
2957 if (oldmember == null) {
2958 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
2959 oldmember = basefile.CreateElement ("Member");
2960 oldmember.SetAttribute ("MemberName", dm.MemberName);
2961 members.AppendChild (oldmember);
2962 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
2963 XmlElement ms = basefile.CreateElement ("MemberSignature");
2964 ms.SetAttribute ("Language", key);
2965 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
2966 oldmember.AppendChild (ms);
2968 oldmember.SetAttribute ("__monodocer-seen__", "true");
2969 Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
2974 m = GetMember (type, new DocumentationMember (oldmember));
2976 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2977 type.FullName, dm.MemberSignatures ["C#"]);
2980 oldmember.SetAttribute ("__monodocer-seen__", "true");
2982 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
2983 if (ecmadocs.Name != "Docs")
2984 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
2989 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
2998 abstract class DocumentationImporter {
3000 public abstract void ImportDocumentation (DocsNodeInfo info);
3003 class MsxdocDocumentationImporter : DocumentationImporter {
3005 XmlDocument slashdocs;
3007 public MsxdocDocumentationImporter (string file)
3009 var xml = File.ReadAllText (file);
3011 // Ensure Unix line endings
3012 xml = xml.Replace ("\r", "");
3014 slashdocs = new XmlDocument();
3015 slashdocs.LoadXml (xml);
3018 public override void ImportDocumentation (DocsNodeInfo info)
3020 XmlNode elem = GetDocs (info.Member ?? info.Type);
3025 XmlElement e = info.Node;
3027 if (elem.SelectSingleNode("summary") != null)
3028 MDocUpdater.ClearElement(e, "summary");
3029 if (elem.SelectSingleNode("remarks") != null)
3030 MDocUpdater.ClearElement(e, "remarks");
3031 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3032 MDocUpdater.ClearElement(e, "value");
3033 MDocUpdater.ClearElement(e, "returns");
3036 foreach (XmlNode child in elem.ChildNodes) {
3037 switch (child.Name) {
3040 XmlAttribute name = child.Attributes ["name"];
3043 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3045 p2.InnerXml = child.InnerXml;
3048 // Occasionally XML documentation will use <returns/> on
3049 // properties, so let's try to normalize things.
3052 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3053 v.InnerXml = child.InnerXml;
3059 case "permission": {
3060 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3063 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3065 a = e.OwnerDocument.CreateElement (child.Name);
3066 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
3069 a.InnerXml = child.InnerXml;
3073 XmlAttribute cref = child.Attributes ["cref"];
3076 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3078 a = e.OwnerDocument.CreateElement ("altmember");
3079 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
3086 if (child.NodeType == XmlNodeType.Element &&
3087 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3090 MDocUpdater.CopyNode (child, e);
3097 private XmlNode GetDocs (MemberReference member)
3099 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3100 if (slashdocsig != null)
3101 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3106 class EcmaDocumentationImporter : DocumentationImporter {
3110 public EcmaDocumentationImporter (XmlReader ecmaDocs)
3112 this.ecmadocs = ecmaDocs;
3115 public override void ImportDocumentation (DocsNodeInfo info)
3117 if (!ecmadocs.IsStartElement ("Docs")) {
3121 XmlElement e = info.Node;
3123 int depth = ecmadocs.Depth;
3124 ecmadocs.ReadStartElement ("Docs");
3125 while (ecmadocs.Read ()) {
3126 if (ecmadocs.Name == "Docs") {
3127 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3130 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3132 if (!ecmadocs.IsStartElement ())
3134 switch (ecmadocs.Name) {
3137 string name = ecmadocs.GetAttribute ("name");
3140 XmlNode doc = e.SelectSingleNode (
3141 ecmadocs.Name + "[@name='" + name + "']");
3142 string value = ecmadocs.ReadInnerXml ();
3144 doc.InnerXml = value.Replace ("\r", "");
3151 string name = ecmadocs.Name;
3152 string cref = ecmadocs.GetAttribute ("cref");
3155 XmlNode doc = e.SelectSingleNode (
3156 ecmadocs.Name + "[@cref='" + cref + "']");
3157 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3159 doc.InnerXml = value;
3161 XmlElement n = e.OwnerDocument.CreateElement (name);
3162 n.SetAttribute ("cref", cref);
3169 string name = ecmadocs.Name;
3170 string xpath = ecmadocs.Name;
3171 StringList attributes = new StringList (ecmadocs.AttributeCount);
3172 if (ecmadocs.MoveToFirstAttribute ()) {
3174 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3175 } while (ecmadocs.MoveToNextAttribute ());
3176 ecmadocs.MoveToContent ();
3178 if (attributes.Count > 0) {
3179 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3181 XmlNode doc = e.SelectSingleNode (xpath);
3182 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3184 doc.InnerXml = value;
3187 XmlElement n = e.OwnerDocument.CreateElement (name);
3189 foreach (string a in attributes) {
3190 int eq = a.IndexOf ('=');
3191 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3202 class DocumentationMember {
3203 public StringToStringMap MemberSignatures = new StringToStringMap ();
3204 public string ReturnType;
3205 public StringList Parameters;
3206 public string MemberName;
3207 public string MemberType;
3209 public DocumentationMember (XmlReader reader)
3211 MemberName = reader.GetAttribute ("MemberName");
3212 int depth = reader.Depth;
3214 StringList p = new StringList ();
3216 if (reader.NodeType != XmlNodeType.Element)
3218 switch (reader.Name) {
3219 case "MemberSignature":
3220 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3223 MemberType = reader.ReadElementString ();
3226 if (reader.Depth == depth + 2)
3227 ReturnType = reader.ReadElementString ();
3230 if (reader.Depth == depth + 2)
3231 p.Add (reader.GetAttribute ("Type"));
3234 if (reader.Depth == depth + 1)
3238 } while (go && reader.Read () && reader.Depth >= depth);
3244 public DocumentationMember (XmlNode node)
3246 MemberName = node.Attributes ["MemberName"].Value;
3247 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3248 XmlAttribute l = n.Attributes ["Language"];
3249 XmlAttribute v = n.Attributes ["Value"];
3250 if (l != null && v != null)
3251 MemberSignatures [l.Value] = v.Value;
3253 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3254 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
3256 ReturnType = rt.InnerText;
3257 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
3259 Parameters = new StringList (p.Count);
3260 for (int i = 0; i < p.Count; ++i)
3261 Parameters.Add (p [i].Attributes ["Type"].Value);
3266 public class DynamicParserContext {
3267 public ReadOnlyCollection<bool> TransformFlags;
3268 public int TransformIndex;
3270 public DynamicParserContext (ICustomAttributeProvider provider)
3273 if (provider.HasCustomAttributes &&
3274 (da = (provider.CustomAttributes.Cast<CustomAttribute>()
3275 .SingleOrDefault (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))) != null) {
3276 CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
3277 ? new CustomAttributeArgument [0]
3278 : (CustomAttributeArgument[]) da.ConstructorArguments [0].Value;
3280 TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool) t.Value).ToArray());
3285 public enum MemberFormatterState {
3287 WithinGenericTypeParameters,
3290 public abstract class MemberFormatter {
3292 public virtual string Language {
3296 public string GetName (MemberReference member)
3298 return GetName (member, null);
3301 public virtual string GetName (MemberReference member, DynamicParserContext context)
3303 TypeReference type = member as TypeReference;
3305 return GetTypeName (type, context);
3306 MethodReference method = member as MethodReference;
3307 if (method != null && method.Name == ".ctor") // method.IsConstructor
3308 return GetConstructorName (method);
3310 return GetMethodName (method);
3311 PropertyReference prop = member as PropertyReference;
3313 return GetPropertyName (prop);
3314 FieldReference field = member as FieldReference;
3316 return GetFieldName (field);
3317 EventReference e = member as EventReference;
3319 return GetEventName (e);
3320 throw new NotSupportedException ("Can't handle: " +
3321 (member == null ? "null" : member.GetType().ToString()));
3324 protected virtual string GetTypeName (TypeReference type)
3326 return GetTypeName (type, null);
3329 protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
3332 throw new ArgumentNullException ("type");
3333 return _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
3336 protected virtual char[] ArrayDelimeters {
3337 get {return new char[]{'[', ']'};}
3340 protected virtual MemberFormatterState MemberFormatterState { get; set; }
3342 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3344 if (type is ArrayType) {
3345 TypeSpecification spec = type as TypeSpecification;
3346 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
3347 return AppendArrayModifiers (buf, (ArrayType) type);
3349 if (type is ByReferenceType) {
3350 return AppendRefTypeName (buf, type, context);
3352 if (type is PointerType) {
3353 return AppendPointerTypeName (buf, type, context);
3355 AppendNamespace (buf, type);
3356 if (type is GenericParameter) {
3357 return AppendTypeName (buf, type, context);
3359 GenericInstanceType genInst = type as GenericInstanceType;
3360 if (type.GenericParameters.Count == 0 &&
3361 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3362 return AppendFullTypeName (buf, type, context);
3364 return AppendGenericType (buf, type, context);
3367 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3369 string ns = DocUtils.GetNamespace (type);
3370 if (ns != null && ns.Length > 0)
3371 buf.Append (ns).Append ('.');
3375 protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3377 if (type.DeclaringType != null)
3378 AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
3379 return AppendTypeName (buf, type, context);
3382 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3384 if (context != null)
3385 context.TransformIndex++;
3386 return AppendTypeName (buf, type.Name);
3389 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3391 int n = typename.IndexOf ("`");
3393 return buf.Append (typename.Substring (0, n));
3394 return buf.Append (typename);
3397 protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
3399 buf.Append (ArrayDelimeters [0]);
3400 int rank = array.Rank;
3402 buf.Append (new string (',', rank-1));
3403 return buf.Append (ArrayDelimeters [1]);
3406 protected virtual string RefTypeModifier {
3410 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3412 TypeSpecification spec = type as TypeSpecification;
3413 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
3414 .Append (RefTypeModifier);
3417 protected virtual string PointerModifier {
3421 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3423 TypeSpecification spec = type as TypeSpecification;
3424 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
3425 .Append (PointerModifier);
3428 protected virtual char[] GenericTypeContainer {
3429 get {return new char[]{'<', '>'};}
3432 protected virtual char NestedTypeSeparator {
3436 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
3438 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3439 type is GenericInstanceType ? type.GetElementType () : type);
3440 List<TypeReference> genArgs = GetGenericArguments (type);
3443 bool insertNested = false;
3444 foreach (var decl in decls) {
3445 TypeReference declDef = decl.Resolve () ?? decl;
3447 buf.Append (NestedTypeSeparator);
3449 insertNested = true;
3450 AppendTypeName (buf, declDef, context);
3451 int ac = DocUtils.GetGenericArgumentCount (declDef);
3455 buf.Append (GenericTypeContainer [0]);
3456 var origState = MemberFormatterState;
3457 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3458 _AppendTypeName (buf, genArgs [argIdx++], context);
3459 for (int i = 1; i < c; ++i) {
3460 _AppendTypeName (buf.Append (","), genArgs [argIdx++], context);
3462 MemberFormatterState = origState;
3463 buf.Append (GenericTypeContainer [1]);
3469 protected List<TypeReference> GetGenericArguments (TypeReference type)
3471 var args = new List<TypeReference> ();
3472 GenericInstanceType inst = type as GenericInstanceType;
3474 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
3476 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
3480 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3485 protected virtual string GetConstructorName (MethodReference constructor)
3487 return constructor.Name;
3490 protected virtual string GetMethodName (MethodReference method)
3495 protected virtual string GetPropertyName (PropertyReference property)
3497 return property.Name;
3500 protected virtual string GetFieldName (FieldReference field)
3505 protected virtual string GetEventName (EventReference e)
3510 public virtual string GetDeclaration (MemberReference member)
3513 throw new ArgumentNullException ("member");
3514 TypeDefinition type = member as TypeDefinition;
3516 return GetTypeDeclaration (type);
3517 MethodDefinition method = member as MethodDefinition;
3518 if (method != null && method.IsConstructor)
3519 return GetConstructorDeclaration (method);
3521 return GetMethodDeclaration (method);
3522 PropertyDefinition prop = member as PropertyDefinition;
3524 return GetPropertyDeclaration (prop);
3525 FieldDefinition field = member as FieldDefinition;
3527 return GetFieldDeclaration (field);
3528 EventDefinition e = member as EventDefinition;
3530 return GetEventDeclaration (e);
3531 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3534 protected virtual string GetTypeDeclaration (TypeDefinition type)
3537 throw new ArgumentNullException ("type");
3538 StringBuilder buf = new StringBuilder (type.Name.Length);
3539 _AppendTypeName (buf, type, null);
3540 AppendGenericTypeConstraints (buf, type);
3541 return buf.ToString ();
3544 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
3546 return GetConstructorName (constructor);
3549 protected virtual string GetMethodDeclaration (MethodDefinition method)
3551 if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
3552 ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
3555 // Special signature for destructors.
3556 if (method.Name == "Finalize" && method.Parameters.Count == 0)
3557 return GetFinalizerName (method);
3559 StringBuilder buf = new StringBuilder ();
3561 AppendVisibility (buf, method);
3562 if (buf.Length == 0 &&
3563 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3566 AppendModifiers (buf, method);
3568 if (buf.Length != 0)
3570 buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
3572 AppendMethodName (buf, method);
3573 AppendGenericMethod (buf, method).Append (" ");
3574 AppendParameters (buf, method, method.Parameters);
3575 AppendGenericMethodConstraints (buf, method);
3576 return buf.ToString ();
3579 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3581 return buf.Append (method.Name);
3584 protected virtual string GetFinalizerName (MethodDefinition method)
3589 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3594 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3599 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3604 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
3609 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3614 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
3616 return GetPropertyName (property);
3619 protected virtual string GetFieldDeclaration (FieldDefinition field)
3621 return GetFieldName (field);
3624 protected virtual string GetEventDeclaration (EventDefinition e)
3626 return GetEventName (e);
3630 class ILFullMemberFormatter : MemberFormatter {
3632 public override string Language {
3633 get {return "ILAsm";}
3636 protected override char NestedTypeSeparator {
3642 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3644 if (GetBuiltinType (type.FullName) != null)
3646 string ns = DocUtils.GetNamespace (type);
3647 if (ns != null && ns.Length > 0) {
3648 if (type.IsValueType)
3649 buf.Append ("valuetype ");
3651 buf.Append ("class ");
3652 buf.Append (ns).Append ('.');
3657 private static string GetBuiltinType (string t)
3660 case "System.Byte": return "unsigned int8";
3661 case "System.SByte": return "int8";
3662 case "System.Int16": return "int16";
3663 case "System.Int32": return "int32";
3664 case "System.Int64": return "int64";
3665 case "System.IntPtr": return "native int";
3667 case "System.UInt16": return "unsigned int16";
3668 case "System.UInt32": return "unsigned int32";
3669 case "System.UInt64": return "unsigned int64";
3670 case "System.UIntPtr": return "native unsigned int";
3672 case "System.Single": return "float32";
3673 case "System.Double": return "float64";
3674 case "System.Boolean": return "bool";
3675 case "System.Char": return "char";
3676 case "System.Void": return "void";
3677 case "System.String": return "string";
3678 case "System.Object": return "object";
3683 protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
3685 return buf.Append (typename);
3688 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3690 if (type is GenericParameter) {
3691 AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
3695 string s = GetBuiltinType (type.FullName);
3697 return buf.Append (s);
3699 return base.AppendTypeName (buf, type, context);
3702 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
3704 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
3705 return buf.Append (type.Owner is TypeReference ? "!" : "!!");
3707 GenericParameterAttributes attrs = type.Attributes;
3708 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
3709 buf.Append ("class ");
3710 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
3711 buf.Append ("struct ");
3712 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
3713 buf.Append (".ctor ");
3714 IList<TypeReference> constraints = type.Constraints;
3715 MemberFormatterState = 0;
3716 if (constraints.Count > 0) {
3717 var full = new ILFullMemberFormatter ();
3718 buf.Append ("(").Append (full.GetName (constraints [0]));
3719 for (int i = 1; i < constraints.Count; ++i) {
3720 buf.Append (", ").Append (full.GetName (constraints [i]));
3724 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3726 if ((attrs & GenericParameterAttributes.Covariant) != 0)
3728 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
3733 protected override string GetTypeDeclaration (TypeDefinition type)
3735 string visibility = GetTypeVisibility (type.Attributes);
3736 if (visibility == null)
3739 StringBuilder buf = new StringBuilder ();
3741 buf.Append (".class ");
3743 buf.Append ("nested ");
3744 buf.Append (visibility).Append (" ");
3745 if (type.IsInterface)
3746 buf.Append ("interface ");
3747 if (type.IsSequentialLayout)
3748 buf.Append ("sequential ");
3749 if (type.IsAutoLayout)
3750 buf.Append ("auto ");
3751 if (type.IsAnsiClass)
3752 buf.Append ("ansi ");
3753 if (type.IsAbstract)
3754 buf.Append ("abstract ");
3755 if (type.IsSerializable)
3756 buf.Append ("serializable ");
3758 buf.Append ("sealed ");
3759 if (type.IsBeforeFieldInit)
3760 buf.Append ("beforefieldinit ");
3761 var state = MemberFormatterState;
3762 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3763 buf.Append (GetName (type));
3764 MemberFormatterState = state;
3765 var full = new ILFullMemberFormatter ();
3766 if (type.BaseType != null) {
3767 buf.Append (" extends ");
3768 if (type.BaseType.FullName == "System.Object")
3769 buf.Append ("System.Object");
3771 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
3774 foreach (var name in type.Interfaces
3775 .Select (i => full.GetName (i))
3776 .OrderBy (n => n)) {
3778 buf.Append (" implements ");
3787 return buf.ToString ();
3790 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
3792 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3793 type is GenericInstanceType ? type.GetElementType () : type);
3795 foreach (var decl in decls) {
3796 TypeReference declDef = decl.Resolve () ?? decl;
3798 buf.Append (NestedTypeSeparator);
3801 AppendTypeName (buf, declDef, context);
3805 foreach (TypeReference arg in GetGenericArguments (type)) {
3809 _AppendTypeName (buf, arg, context);
3815 static string GetTypeVisibility (TypeAttributes ta)
3817 switch (ta & TypeAttributes.VisibilityMask) {
3818 case TypeAttributes.Public:
3819 case TypeAttributes.NestedPublic:
3822 case TypeAttributes.NestedFamily:
3823 case TypeAttributes.NestedFamORAssem:
3831 protected override string GetConstructorDeclaration (MethodDefinition constructor)
3833 return GetMethodDeclaration (constructor);
3836 protected override string GetMethodDeclaration (MethodDefinition method)
3838 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
3841 var buf = new StringBuilder ();
3842 buf.Append (".method ");
3843 AppendVisibility (buf, method);
3844 if (method.IsStatic)
3845 buf.Append ("static ");
3846 if (method.IsHideBySig)
3847 buf.Append ("hidebysig ");
3848 if (method.IsPInvokeImpl) {
3849 var info = method.PInvokeInfo;
3850 buf.Append ("pinvokeimpl (\"")
3851 .Append (info.Module.Name)
3852 .Append ("\" as \"")
3853 .Append (info.EntryPoint)
3855 if (info.IsCharSetAuto)
3856 buf.Append (" auto");
3857 if (info.IsCharSetUnicode)
3858 buf.Append (" unicode");
3859 if (info.IsCharSetAnsi)
3860 buf.Append (" ansi");
3861 if (info.IsCallConvCdecl)
3862 buf.Append (" cdecl");
3863 if (info.IsCallConvStdCall)
3864 buf.Append (" stdcall");
3865 if (info.IsCallConvWinapi)
3866 buf.Append (" winapi");
3867 if (info.IsCallConvThiscall)
3868 buf.Append (" thiscall");
3869 if (info.SupportsLastError)
3870 buf.Append (" lasterr");
3873 if (method.IsSpecialName)
3874 buf.Append ("specialname ");
3875 if (method.IsRuntimeSpecialName)
3876 buf.Append ("rtspecialname ");
3877 if (method.IsNewSlot)
3878 buf.Append ("newslot ");
3879 if (method.IsVirtual)
3880 buf.Append ("virtual ");
3881 if (!method.IsStatic)
3882 buf.Append ("instance ");
3883 _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
3885 .Append (method.Name);
3886 if (method.IsGenericMethod ()) {
3887 var state = MemberFormatterState;
3888 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3889 IList<GenericParameter> args = method.GenericParameters;
3890 if (args.Count > 0) {
3892 _AppendTypeName (buf, args [0], null);
3893 for (int i = 1; i < args.Count; ++i)
3894 _AppendTypeName (buf.Append (", "), args [i], null);
3897 MemberFormatterState = state;
3902 for (int i = 0; i < method.Parameters.Count; ++i) {
3906 _AppendTypeName (buf, method.Parameters [i].ParameterType, new DynamicParserContext (method.Parameters [i]));
3908 buf.Append (method.Parameters [i].Name);
3912 buf.Append (" cil");
3913 if (method.IsRuntime)
3914 buf.Append (" runtime");
3915 if (method.IsManaged)
3916 buf.Append (" managed");
3918 return buf.ToString ();
3921 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3923 if (DocUtils.IsExplicitlyImplemented (method)) {
3924 TypeReference iface;
3925 MethodReference ifaceMethod;
3926 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3927 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3929 .Append (ifaceMethod.Name);
3931 return base.AppendMethodName (buf, method);
3934 protected override string RefTypeModifier {
3938 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3940 if (method.IsPublic)
3941 return buf.Append ("public ");
3942 if (method.IsFamilyAndAssembly)
3943 return buf.Append ("familyandassembly");
3944 if (method.IsFamilyOrAssembly)
3945 return buf.Append ("familyorassembly");
3946 if (method.IsFamily)
3947 return buf.Append ("family");
3951 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3953 string modifiers = String.Empty;
3954 if (method.IsStatic) modifiers += " static";
3955 if (method.IsVirtual && !method.IsAbstract) {
3956 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3957 else modifiers += " override";
3959 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
3960 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
3961 if (method.IsFinal) modifiers += " sealed";
3962 if (modifiers == " virtual sealed") modifiers = "";
3964 return buf.Append (modifiers);
3967 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3969 if (method.IsGenericMethod ()) {
3970 IList<GenericParameter> args = method.GenericParameters;
3971 if (args.Count > 0) {
3973 buf.Append (args [0].Name);
3974 for (int i = 1; i < args.Count; ++i)
3975 buf.Append (",").Append (args [i].Name);
3982 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
3984 return AppendParameters (buf, method, parameters, '(', ')');
3987 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
3991 if (parameters.Count > 0) {
3992 if (DocUtils.IsExtensionMethod (method))
3993 buf.Append ("this ");
3994 AppendParameter (buf, parameters [0]);
3995 for (int i = 1; i < parameters.Count; ++i) {
3997 AppendParameter (buf, parameters [i]);
4001 return buf.Append (end);
4004 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4006 if (parameter.ParameterType is ByReferenceType) {
4007 if (parameter.IsOut)
4008 buf.Append ("out ");
4010 buf.Append ("ref ");
4012 buf.Append (GetName (parameter.ParameterType)).Append (" ");
4013 return buf.Append (parameter.Name);
4016 protected override string GetPropertyDeclaration (PropertyDefinition property)
4018 MethodDefinition gm = null, sm = null;
4020 string get_visible = null;
4021 if ((gm = property.GetMethod) != null &&
4022 (DocUtils.IsExplicitlyImplemented (gm) ||
4023 (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
4024 get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
4025 string set_visible = null;
4026 if ((sm = property.SetMethod) != null &&
4027 (DocUtils.IsExplicitlyImplemented (sm) ||
4028 (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
4029 set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
4031 if ((set_visible == null) && (get_visible == null))
4034 StringBuilder buf = new StringBuilder ()
4035 .Append (".property ");
4036 if (!(gm ?? sm).IsStatic)
4037 buf.Append ("instance ");
4038 _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
4039 buf.Append (' ').Append (property.Name);
4040 if (!property.HasParameters || property.Parameters.Count == 0)
4041 return buf.ToString ();
4045 foreach (ParameterDefinition p in property.Parameters) {
4049 _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
4053 return buf.ToString ();
4056 protected override string GetFieldDeclaration (FieldDefinition field)
4058 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4059 if (declType.IsEnum && field.Name == "value__")
4060 return null; // This member of enums aren't documented.
4062 StringBuilder buf = new StringBuilder ();
4063 AppendFieldVisibility (buf, field);
4064 if (buf.Length == 0)
4067 buf.Insert (0, ".field ");
4070 buf.Append ("static ");
4071 if (field.IsInitOnly)
4072 buf.Append ("initonly ");
4073 if (field.IsLiteral)
4074 buf.Append ("literal ");
4075 _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
4076 buf.Append (' ').Append (field.Name);
4077 AppendFieldValue (buf, field);
4079 return buf.ToString ();
4082 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4085 return buf.Append ("public ");
4086 if (field.IsFamilyAndAssembly)
4087 return buf.Append ("familyandassembly ");
4088 if (field.IsFamilyOrAssembly)
4089 return buf.Append ("familyorassembly ");
4091 return buf.Append ("family ");
4095 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4097 // enums have a value__ field, which we ignore
4098 if (field.DeclaringType.IsGenericType ())
4100 if (field.HasConstant && field.IsLiteral) {
4103 val = field.Constant;
4108 buf.Append (" = ").Append ("null");
4109 else if (val is Enum)
4111 .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4113 .Append (val.ToString ())
4115 else if (val is IFormattable) {
4116 string value = ((IFormattable)val).ToString();
4119 buf.Append ("\"" + value + "\"");
4121 buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4130 protected override string GetEventDeclaration (EventDefinition e)
4132 StringBuilder buf = new StringBuilder ();
4133 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4138 buf.Append (".event ")
4139 .Append (GetName (e.EventType))
4143 return buf.ToString ();
4147 class ILMemberFormatter : ILFullMemberFormatter {
4148 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4154 class CSharpFullMemberFormatter : MemberFormatter {
4156 public override string Language {
4160 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4162 string ns = DocUtils.GetNamespace (type);
4163 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
4164 buf.Append (ns).Append ('.');
4168 private string GetCSharpType (string t)
4171 case "System.Byte": return "byte";
4172 case "System.SByte": return "sbyte";
4173 case "System.Int16": return "short";
4174 case "System.Int32": return "int";
4175 case "System.Int64": return "long";
4177 case "System.UInt16": return "ushort";
4178 case "System.UInt32": return "uint";
4179 case "System.UInt64": return "ulong";
4181 case "System.Single": return "float";
4182 case "System.Double": return "double";
4183 case "System.Decimal": return "decimal";
4184 case "System.Boolean": return "bool";
4185 case "System.Char": return "char";
4186 case "System.Void": return "void";
4187 case "System.String": return "string";
4188 case "System.Object": return "object";
4193 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4195 if (context != null && context.TransformFlags != null &&
4196 (context.TransformFlags.Count == 0 || context.TransformFlags [context.TransformIndex])) {
4197 context.TransformIndex++;
4198 return buf.Append ("dynamic");
4201 if (type is GenericParameter)
4202 return AppendGenericParameterConstraints (buf, (GenericParameter) type, context).Append (type.Name);
4203 string t = type.FullName;
4204 if (!t.StartsWith ("System.")) {
4205 return base.AppendTypeName (buf, type, context);
4208 string s = GetCSharpType (t);
4210 if (context != null)
4211 context.TransformIndex++;
4212 return buf.Append (s);
4215 return base.AppendTypeName (buf, type, context);
4218 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
4220 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
4222 GenericParameterAttributes attrs = type.Attributes;
4223 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
4224 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
4228 buf.Append ("out ");
4232 protected override string GetTypeDeclaration (TypeDefinition type)
4234 string visibility = GetTypeVisibility (type.Attributes);
4235 if (visibility == null)
4238 StringBuilder buf = new StringBuilder ();
4240 buf.Append (visibility);
4243 MemberFormatter full = new CSharpFullMemberFormatter ();
4245 if (DocUtils.IsDelegate (type)) {
4246 buf.Append("delegate ");
4247 MethodDefinition invoke = type.GetMethod ("Invoke");
4248 buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
4249 buf.Append (GetName (type));
4250 AppendParameters (buf, invoke, invoke.Parameters);
4251 AppendGenericTypeConstraints (buf, type);
4254 return buf.ToString();
4257 if (type.IsAbstract && !type.IsInterface)
4258 buf.Append("abstract ");
4259 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
4260 buf.Append("sealed ");
4261 buf.Replace ("abstract sealed", "static");
4263 buf.Append (GetTypeKind (type));
4265 buf.Append (GetCSharpType (type.FullName) == null
4270 TypeReference basetype = type.BaseType;
4271 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
4274 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
4275 .Select (iface => full.GetName (iface))
4279 if (basetype != null || interface_names.Count > 0)
4282 if (basetype != null) {
4283 buf.Append (full.GetName (basetype));
4284 if (interface_names.Count > 0)
4288 for (int i = 0; i < interface_names.Count; i++){
4291 buf.Append (interface_names [i]);
4293 AppendGenericTypeConstraints (buf, type);
4296 return buf.ToString ();
4299 static string GetTypeKind (TypeDefinition t)
4305 if (t.IsClass || t.FullName == "System.Enum")
4309 throw new ArgumentException(t.FullName);
4312 static string GetTypeVisibility (TypeAttributes ta)
4314 switch (ta & TypeAttributes.VisibilityMask) {
4315 case TypeAttributes.Public:
4316 case TypeAttributes.NestedPublic:
4319 case TypeAttributes.NestedFamily:
4320 case TypeAttributes.NestedFamORAssem:
4328 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4330 if (type.GenericParameters.Count == 0)
4332 return AppendConstraints (buf, type.GenericParameters);
4335 private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
4337 foreach (GenericParameter genArg in genArgs) {
4338 GenericParameterAttributes attrs = genArg.Attributes;
4339 IList<TypeReference> constraints = genArg.Constraints;
4340 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
4343 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
4344 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
4345 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
4348 if (!isref && !isvt && !isnew && constraints.Count == 0)
4350 buf.Append (" where ").Append (genArg.Name).Append (" : ");
4352 buf.Append ("class");
4356 buf.Append ("struct");
4359 if (constraints.Count > 0 && !isvt) {
4362 buf.Append (GetTypeName (constraints [0]));
4363 for (int i = 1; i < constraints.Count; ++i)
4364 buf.Append (", ").Append (GetTypeName (constraints [i]));
4366 if (isnew && !isvt) {
4369 buf.Append ("new()");
4375 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4377 StringBuilder buf = new StringBuilder ();
4378 AppendVisibility (buf, constructor);
4379 if (buf.Length == 0)
4383 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
4384 AppendParameters (buf, constructor, constructor.Parameters);
4387 return buf.ToString ();
4390 protected override string GetMethodDeclaration (MethodDefinition method)
4392 string decl = base.GetMethodDeclaration (method);
4398 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4400 if (DocUtils.IsExplicitlyImplemented (method)) {
4401 TypeReference iface;
4402 MethodReference ifaceMethod;
4403 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4404 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4406 .Append (ifaceMethod.Name);
4408 return base.AppendMethodName (buf, method);
4411 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4413 if (method.GenericParameters.Count == 0)
4415 return AppendConstraints (buf, method.GenericParameters);
4418 protected override string RefTypeModifier {
4422 protected override string GetFinalizerName (MethodDefinition method)
4424 return "~" + method.DeclaringType.Name + " ()";
4427 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4431 if (method.IsPublic)
4432 return buf.Append ("public");
4433 if (method.IsFamily || method.IsFamilyOrAssembly)
4434 return buf.Append ("protected");
4438 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4440 string modifiers = String.Empty;
4441 if (method.IsStatic) modifiers += " static";
4442 if (method.IsVirtual && !method.IsAbstract) {
4443 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4444 else modifiers += " override";
4446 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4447 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4448 if (method.IsFinal) modifiers += " sealed";
4449 if (modifiers == " virtual sealed") modifiers = "";
4451 return buf.Append (modifiers);
4454 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4456 if (method.IsGenericMethod ()) {
4457 IList<GenericParameter> args = method.GenericParameters;
4458 if (args.Count > 0) {
4460 buf.Append (args [0].Name);
4461 for (int i = 1; i < args.Count; ++i)
4462 buf.Append (",").Append (args [i].Name);
4469 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4471 return AppendParameters (buf, method, parameters, '(', ')');
4474 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4478 if (parameters.Count > 0) {
4479 if (DocUtils.IsExtensionMethod (method))
4480 buf.Append ("this ");
4481 AppendParameter (buf, parameters [0]);
4482 for (int i = 1; i < parameters.Count; ++i) {
4484 AppendParameter (buf, parameters [i]);
4488 return buf.Append (end);
4491 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4493 if (parameter.ParameterType is ByReferenceType) {
4494 if (parameter.IsOut)
4495 buf.Append ("out ");
4497 buf.Append ("ref ");
4499 buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
4500 buf.Append (parameter.Name);
4501 if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant) {
4502 buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
4507 protected override string GetPropertyDeclaration (PropertyDefinition property)
4509 MethodDefinition method;
4511 string get_visible = null;
4512 if ((method = property.GetMethod) != null &&
4513 (DocUtils.IsExplicitlyImplemented (method) ||
4514 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
4515 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
4516 string set_visible = null;
4517 if ((method = property.SetMethod) != null &&
4518 (DocUtils.IsExplicitlyImplemented (method) ||
4519 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
4520 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
4522 if ((set_visible == null) && (get_visible == null))
4526 StringBuilder buf = new StringBuilder ();
4527 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
4528 buf.Append (visibility = get_visible);
4529 else if (set_visible != null && get_visible == null)
4530 buf.Append (visibility = set_visible);
4532 buf.Append (visibility = "public");
4534 // Pick an accessor to use for static/virtual/override/etc. checks.
4535 method = property.SetMethod;
4537 method = property.GetMethod;
4539 string modifiers = String.Empty;
4540 if (method.IsStatic) modifiers += " static";
4541 if (method.IsVirtual && !method.IsAbstract) {
4542 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
4543 modifiers += " virtual";
4545 modifiers += " override";
4547 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
4548 if (method.IsAbstract && !declDef.IsInterface)
4549 modifiers += " abstract";
4551 modifiers += " sealed";
4552 if (modifiers == " virtual sealed")
4554 buf.Append (modifiers).Append (' ');
4556 buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
4558 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
4559 string name = property.Name;
4560 foreach (MemberReference mi in defs) {
4561 if (mi == property) {
4566 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
4568 if (property.Parameters.Count != 0) {
4569 AppendParameters (buf, method, property.Parameters, '[', ']');
4573 if (get_visible != null) {
4574 if (get_visible != visibility)
4575 buf.Append (' ').Append (get_visible);
4576 buf.Append (" get;");
4578 if (set_visible != null) {
4579 if (set_visible != visibility)
4580 buf.Append (' ').Append (set_visible);
4581 buf.Append (" set;");
4585 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
4588 protected override string GetFieldDeclaration (FieldDefinition field)
4590 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4591 if (declType.IsEnum && field.Name == "value__")
4592 return null; // This member of enums aren't documented.
4594 StringBuilder buf = new StringBuilder ();
4595 AppendFieldVisibility (buf, field);
4596 if (buf.Length == 0)
4599 if (declType.IsEnum)
4602 if (field.IsStatic && !field.IsLiteral)
4603 buf.Append (" static");
4604 if (field.IsInitOnly)
4605 buf.Append (" readonly");
4606 if (field.IsLiteral)
4607 buf.Append (" const");
4609 buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
4610 buf.Append (field.Name);
4611 AppendFieldValue (buf, field);
4614 return buf.ToString ();
4617 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4620 return buf.Append ("public");
4621 if (field.IsFamily || field.IsFamilyOrAssembly)
4622 return buf.Append ("protected");
4626 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4628 // enums have a value__ field, which we ignore
4629 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
4630 field.DeclaringType.IsGenericType ())
4632 if (field.HasConstant && field.IsLiteral) {
4635 val = field.Constant;
4640 buf.Append (" = ").Append ("null");
4641 else if (val is Enum)
4642 buf.Append (" = ").Append (val.ToString ());
4643 else if (val is IFormattable) {
4644 string value = ((IFormattable)val).ToString();
4646 value = "\"" + value + "\"";
4647 buf.Append (" = ").Append (value);
4653 protected override string GetEventDeclaration (EventDefinition e)
4655 StringBuilder buf = new StringBuilder ();
4656 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4660 AppendModifiers (buf, e.AddMethod);
4662 buf.Append (" event ");
4663 buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters [0]))).Append (' ');
4664 buf.Append (e.Name).Append (';');
4666 return buf.ToString ();
4670 class CSharpMemberFormatter : CSharpFullMemberFormatter {
4671 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4677 class DocTypeFullMemberFormatter : MemberFormatter {
4678 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
4680 protected override char NestedTypeSeparator {
4685 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
4686 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4692 class SlashDocMemberFormatter : MemberFormatter {
4694 protected override char[] GenericTypeContainer {
4695 get {return new char[]{'{', '}'};}
4698 private bool AddTypeCount = true;
4700 private TypeReference genDeclType;
4701 private MethodReference genDeclMethod;
4703 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4705 if (type is GenericParameter) {
4707 if (genDeclType != null) {
4708 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
4709 for (int i = 0; i < genArgs.Count; ++i) {
4710 if (genArgs [i].Name == type.Name) {
4711 buf.Append ('`').Append (i);
4716 if (genDeclMethod != null) {
4717 IList<GenericParameter> genArgs = null;
4718 if (genDeclMethod.IsGenericMethod ()) {
4719 genArgs = genDeclMethod.GenericParameters;
4720 for (int i = 0; i < genArgs.Count; ++i) {
4721 if (genArgs [i].Name == type.Name) {
4722 buf.Append ("``").Append (i);
4728 if (genDeclType == null && genDeclMethod == null) {
4729 // Probably from within an explicitly implemented interface member,
4730 // where CSC uses parameter names instead of indices (why?), e.g.
4731 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
4732 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
4733 buf.Append (type.Name);
4735 if (buf.Length == l) {
4736 throw new Exception (string.Format (
4737 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
4738 type.Name, genDeclType, genDeclMethod));
4742 base.AppendTypeName (buf, type, context);
4744 int numArgs = type.GenericParameters.Count;
4745 if (type.DeclaringType != null)
4746 numArgs -= type.GenericParameters.Count;
4748 buf.Append ('`').Append (numArgs);
4755 protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
4757 buf.Append (ArrayDelimeters [0]);
4758 int rank = array.Rank;
4761 for (int i = 1; i < rank; ++i) {
4765 return buf.Append (ArrayDelimeters [1]);
4768 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4771 base.AppendGenericType (buf, type, context);
4773 AppendType (buf, type, context);
4777 private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4779 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
4780 bool insertNested = false;
4781 int prevParamCount = 0;
4782 foreach (var decl in decls) {
4784 buf.Append (NestedTypeSeparator);
4785 insertNested = true;
4786 base.AppendTypeName (buf, decl, context);
4787 int argCount = DocUtils.GetGenericArgumentCount (decl);
4788 int numArgs = argCount - prevParamCount;
4789 prevParamCount = argCount;
4791 buf.Append ('`').Append (numArgs);
4796 public override string GetDeclaration (MemberReference member)
4798 TypeReference r = member as TypeReference;
4800 return "T:" + GetTypeName (r);
4802 return base.GetDeclaration (member);
4805 protected override string GetConstructorName (MethodReference constructor)
4807 return GetMethodDefinitionName (constructor, "#ctor");
4810 protected override string GetMethodName (MethodReference method)
4813 MethodDefinition methodDef = method as MethodDefinition;
4814 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
4817 TypeReference iface;
4818 MethodReference ifaceMethod;
4819 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
4820 AddTypeCount = false;
4821 name = GetTypeName (iface) + "." + ifaceMethod.Name;
4822 AddTypeCount = true;
4824 return GetMethodDefinitionName (method, name);
4827 private string GetMethodDefinitionName (MethodReference method, string name)
4829 StringBuilder buf = new StringBuilder ();
4830 buf.Append (GetTypeName (method.DeclaringType));
4832 buf.Append (name.Replace (".", "#"));
4833 if (method.IsGenericMethod ()) {
4834 IList<GenericParameter> genArgs = method.GenericParameters;
4835 if (genArgs.Count > 0)
4836 buf.Append ("``").Append (genArgs.Count);
4838 IList<ParameterDefinition> parameters = method.Parameters;
4840 genDeclType = method.DeclaringType;
4841 genDeclMethod = method;
4842 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
4846 genDeclMethod = null;
4848 return buf.ToString ();
4851 private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
4853 if (parameters.Count == 0)
4858 AppendParameter (buf, genArgs, parameters [0]);
4859 for (int i = 1; i < parameters.Count; ++i) {
4861 AppendParameter (buf, genArgs, parameters [i]);
4864 return buf.Append (')');
4867 private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
4869 AddTypeCount = false;
4870 buf.Append (GetTypeName (parameter.ParameterType));
4871 AddTypeCount = true;
4875 protected override string GetPropertyName (PropertyReference property)
4879 PropertyDefinition propertyDef = property as PropertyDefinition;
4880 MethodDefinition method = null;
4881 if (propertyDef != null)
4882 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
4883 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
4884 name = property.Name;
4886 TypeReference iface;
4887 MethodReference ifaceMethod;
4888 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4889 AddTypeCount = false;
4890 name = string.Join ("#", new string[]{
4891 GetTypeName (iface).Replace (".", "#"),
4892 DocUtils.GetMember (property.Name)
4894 AddTypeCount = true;
4897 StringBuilder buf = new StringBuilder ();
4898 buf.Append (GetName (property.DeclaringType));
4901 IList<ParameterDefinition> parameters = property.Parameters;
4902 if (parameters.Count > 0) {
4903 genDeclType = property.DeclaringType;
4905 IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
4906 AppendParameter (buf, genArgs, parameters [0]);
4907 for (int i = 1; i < parameters.Count; ++i) {
4909 AppendParameter (buf, genArgs, parameters [i]);
4914 return buf.ToString ();
4917 protected override string GetFieldName (FieldReference field)
4919 return string.Format ("{0}.{1}",
4920 GetName (field.DeclaringType), field.Name);
4923 protected override string GetEventName (EventReference e)
4925 return string.Format ("{0}.{1}",
4926 GetName (e.DeclaringType), e.Name);
4929 protected override string GetTypeDeclaration (TypeDefinition type)
4931 string name = GetName (type);
4937 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4939 string name = GetName (constructor);
4945 protected override string GetMethodDeclaration (MethodDefinition method)
4947 string name = GetName (method);
4950 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4951 genDeclType = method.DeclaringType;
4952 genDeclMethod = method;
4953 name += "~" + GetName (method.ReturnType);
4955 genDeclMethod = null;
4960 protected override string GetPropertyDeclaration (PropertyDefinition property)
4962 string name = GetName (property);
4968 protected override string GetFieldDeclaration (FieldDefinition field)
4970 string name = GetName (field);
4976 protected override string GetEventDeclaration (EventDefinition e)
4978 string name = GetName (e);
4985 class FileNameMemberFormatter : SlashDocMemberFormatter {
4986 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4991 protected override char NestedTypeSeparator {