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 HashSet<string> forwardedTypes = new HashSet<string> ();
65 public override void Run (IEnumerable<string> args)
67 show_exceptions = DebugOutput;
68 var types = new List<string> ();
69 var p = new OptionSet () {
71 "Delete removed members from the XML files.",
72 v => delete = v != null },
74 "Document potential exceptions that members can generate. {SOURCES} " +
75 "is a comma-separated list of:\n" +
76 " asm Method calls in same assembly\n" +
77 " depasm Method calls in dependent assemblies\n" +
78 " all Record all possible exceptions\n" +
79 " added Modifier; only create <exception/>s\n" +
80 " for NEW types/members\n" +
81 "If nothing is specified, then only exceptions from the member will " +
83 v => exceptions = ParseExceptionLocations (v) },
85 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
88 case "ignore-missing-types":
89 ignore_missing_types = true;
91 case "no-assembly-versions":
92 no_assembly_versions = true;
95 throw new Exception ("Unsupported flag `" + v + "'.");
98 { "fignore-missing-types",
99 "Do not report an error if a --type=TYPE type\nwas not found.",
100 v => ignore_missing_types = v != null },
101 { "fno-assembly-versions",
102 "Do not generate //AssemblyVersion elements.",
103 v => no_assembly_versions = v != null },
105 "Import documentation from {FILE}.",
106 v => AddImporter (v) },
108 "Check for assembly references in {DIRECTORY}.",
109 v => assemblyResolver.AddSearchDirectory (v) },
111 "Ignored for compatibility with update-ecma-xml.",
114 "Root {DIRECTORY} to generate/update documentation.",
117 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
118 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
119 v => assemblyResolver.AddSearchDirectory (Path.GetDirectoryName (v)) },
121 "Manually specify the assembly {VERSION} that new members were added in.",
124 "Only update documentation for {TYPE}.",
125 v => types.Add (v) },
127 var assemblies = Parse (p, args, "update",
128 "[OPTIONS]+ ASSEMBLIES",
129 "Create or update documentation from ASSEMBLIES.");
130 if (assemblies == null)
132 if (assemblies.Count == 0)
133 Error ("No assemblies specified.");
135 foreach (var dir in assemblies
136 .Where (a => a.Contains (Path.DirectorySeparatorChar))
137 .Select (a => Path.GetDirectoryName (a)))
138 assemblyResolver.AddSearchDirectory (dir);
140 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
143 throw new InvalidOperationException("The --out option is required.");
145 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
147 // Store types that have been forwarded to avoid duplicate generation
148 GatherForwardedTypes ();
150 docEnum = docEnum ?? new DocumentationEnumerator ();
152 // PERFORM THE UPDATES
154 if (types.Count > 0) {
156 DoUpdateTypes (srcPath, types, srcPath);
159 else if (opts.@namespace != null)
160 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
161 Path.Combine (dest_dir, opts.@namespace));
164 DoUpdateAssemblies (srcPath, srcPath);
166 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
169 void AddImporter (string path)
172 XmlReader r = new XmlTextReader (path);
174 while (r.NodeType != XmlNodeType.Element) {
176 Error ("Unable to read XML file: {0}.", path);
178 if (r.LocalName == "doc") {
179 importers.Add (new MsxdocDocumentationImporter (path));
181 else if (r.LocalName == "Libraries") {
182 var ecmadocs = new XmlTextReader (path);
183 docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
184 importers.Add (new EcmaDocumentationImporter (ecmadocs));
187 Error ("Unsupported XML format within {0}.", path);
190 } catch (Exception e) {
191 Environment.ExitCode = 1;
192 Error ("Could not load XML file: {0}.", e.Message);
196 void GatherForwardedTypes ()
198 foreach (var asm in assemblies)
199 foreach (var type in asm.MainModule.ExportedTypes.Where (t => t.IsForwarder).Select (t => t.FullName))
200 forwardedTypes.Add (type);
203 static ExceptionLocations ParseExceptionLocations (string s)
205 ExceptionLocations loc = ExceptionLocations.Member;
208 foreach (var type in s.Split (',')) {
210 case "added": loc |= ExceptionLocations.AddedMembers; break;
211 case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
212 case "asm": loc |= ExceptionLocations.Assembly; break;
213 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
214 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
220 internal void Warning (string format, params object[] args)
222 Message (TraceLevel.Warning, "mdoc: " + format, args);
225 private AssemblyDefinition LoadAssembly (string name)
227 AssemblyDefinition assembly = null;
229 assembly = AssemblyDefinition.ReadAssembly (name, new ReaderParameters { AssemblyResolver = assemblyResolver });
230 } catch (System.IO.FileNotFoundException) { }
232 if (assembly == null)
233 throw new InvalidOperationException("Assembly " + name + " not found.");
238 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
239 OrderTypeAttributes (element);
240 XmlTextWriter writer = new XmlTextWriter(output);
241 writer.Formatting = Formatting.Indented;
242 writer.Indentation = 2;
243 writer.IndentChar = ' ';
244 element.WriteTo(writer);
248 private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
250 Action<string> creator = file => {
251 using (var writer = OpenWrite (file, mode))
255 MdocFile.UpdateFile (filename, creator);
258 private static void OrderTypeAttributes (XmlElement e)
260 foreach (XmlElement type in e.SelectNodes ("//Type")) {
261 OrderTypeAttributes (type.Attributes);
265 static readonly string[] TypeAttributeOrder = {
266 "Name", "FullName", "FullNameSP", "Maintainer"
269 private static void OrderTypeAttributes (XmlAttributeCollection c)
271 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
272 for (int i = 0; i < c.Count; ++i) {
273 XmlAttribute a = c [i];
274 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
275 if (a.Name == TypeAttributeOrder [j]) {
281 for (int i = attrs.Length-1; i >= 0; --i) {
282 XmlAttribute n = attrs [i];
285 XmlAttribute r = null;
286 for (int j = i+1; j < attrs.Length; ++j) {
287 if (attrs [j] != null) {
295 c.InsertBefore (n, r);
299 private XmlDocument CreateIndexStub()
301 XmlDocument index = new XmlDocument();
303 XmlElement index_root = index.CreateElement("Overview");
304 index.AppendChild(index_root);
306 if (assemblies.Count == 0)
307 throw new Exception ("No assembly");
309 XmlElement index_assemblies = index.CreateElement("Assemblies");
310 index_root.AppendChild(index_assemblies);
312 XmlElement index_remarks = index.CreateElement("Remarks");
313 index_remarks.InnerText = "To be added.";
314 index_root.AppendChild(index_remarks);
316 XmlElement index_copyright = index.CreateElement("Copyright");
317 index_copyright.InnerText = "To be added.";
318 index_root.AppendChild(index_copyright);
320 XmlElement index_types = index.CreateElement("Types");
321 index_root.AppendChild(index_types);
326 private static void WriteNamespaceStub(string ns, string outdir) {
327 XmlDocument index = new XmlDocument();
329 XmlElement index_root = index.CreateElement("Namespace");
330 index.AppendChild(index_root);
332 index_root.SetAttribute("Name", ns);
334 XmlElement index_docs = index.CreateElement("Docs");
335 index_root.AppendChild(index_docs);
337 XmlElement index_summary = index.CreateElement("summary");
338 index_summary.InnerText = "To be added.";
339 index_docs.AppendChild(index_summary);
341 XmlElement index_remarks = index.CreateElement("remarks");
342 index_remarks.InnerText = "To be added.";
343 index_docs.AppendChild(index_remarks);
345 WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
346 writer => WriteXml (index.DocumentElement, writer));
349 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
351 var index = CreateIndexForTypes (dest);
353 var found = new HashSet<string> ();
354 foreach (AssemblyDefinition assembly in assemblies) {
355 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames)) {
356 string relpath = DoUpdateType (type, basepath, dest);
360 found.Add (type.FullName);
365 index.Add (assembly);
373 if (ignore_missing_types)
376 var notFound = from n in typenames where !found.Contains (n) select n;
378 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
381 class IndexForTypes {
387 XmlElement index_types;
388 XmlElement index_assemblies;
390 public IndexForTypes (MDocUpdater app, string indexFile, XmlDocument index)
393 this.indexFile = indexFile;
396 index_types = WriteElement (index.DocumentElement, "Types");
397 index_assemblies = WriteElement (index.DocumentElement, "Assemblies");
400 public void Add (AssemblyDefinition assembly)
402 if (index_assemblies.SelectSingleNode ("Assembly[@Name='" + assembly.Name.Name + "']") != null)
405 app.AddIndexAssembly (assembly, index_assemblies);
408 public void Add (TypeDefinition type)
410 app.AddIndexType (type, index_types);
415 SortIndexEntries (index_types);
416 WriteFile (indexFile, FileMode.Create,
417 writer => WriteXml (index.DocumentElement, writer));
421 IndexForTypes CreateIndexForTypes (string dest)
423 string indexFile = Path.Combine (dest, "index.xml");
424 if (File.Exists (indexFile))
426 return new IndexForTypes (this, indexFile, CreateIndexStub ());
429 public string DoUpdateType (TypeDefinition type, string basepath, string dest)
431 if (type.Namespace == null)
432 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
434 if (!IsPublic (type))
437 // Must get the A+B form of the type name.
438 string typename = GetTypeFileName(type);
440 string reltypefile = DocUtils.PathCombine (DocUtils.GetNamespace (type), typename + ".xml");
441 string typefile = Path.Combine (basepath, reltypefile);
442 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
444 string output = null;
447 } else if (dest == "-") {
450 output = Path.Combine (dest, reltypefile);
455 XmlDocument basefile = new XmlDocument();
457 basefile.Load(typefile);
458 } catch (Exception e) {
459 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
462 DoUpdateType2("Updating", basefile, type, output, false);
465 XmlElement td = StubType(type, output);
469 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
472 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
478 public void DoUpdateNS (string ns, string nspath, string outpath)
480 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
481 AssemblyDefinition assembly = assemblies [0];
483 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
484 XmlDocument basefile = new XmlDocument();
485 string typefile = Path.Combine(nspath, file.Name);
487 basefile.Load(typefile);
488 } catch (Exception e) {
489 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
493 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
494 TypeDefinition type = assembly.GetType(typename);
496 Warning ("Type no longer in assembly: " + typename);
500 seenTypes[type] = seenTypes;
501 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false);
504 // Stub types not in the directory
505 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
506 if (type.Namespace != ns || seenTypes.ContainsKey(type))
509 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"));
510 if (td == null) continue;
514 private static string GetTypeFileName (TypeReference type)
516 return filenameFormatter.GetName (type);
519 public static string GetTypeFileName (string typename)
521 StringBuilder filename = new StringBuilder (typename.Length);
525 for (int i = 0; i < typename.Length; ++i) {
526 char c = typename [i];
535 filename.Append ('`').Append ((numArgs+1).ToString());
550 return filename.ToString ();
553 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
555 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
556 index_assembly.SetAttribute ("Name", assembly.Name.Name);
557 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
559 AssemblyNameDefinition name = assembly.Name;
560 if (name.HasPublicKey) {
561 XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
562 var key = new StringBuilder (name.PublicKey.Length*3 + 2);
564 foreach (byte b in name.PublicKey)
565 key.AppendFormat ("{0,2:x2} ", b);
567 pubkey.InnerText = key.ToString ();
568 index_assembly.AppendChild (pubkey);
571 if (!string.IsNullOrEmpty (name.Culture)) {
572 XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
573 culture.InnerText = name.Culture;
574 index_assembly.AppendChild (culture);
577 MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
578 parent.AppendChild(index_assembly);
581 private void AddIndexType (TypeDefinition type, XmlElement index_types)
583 string typename = GetTypeFileName(type);
585 // Add namespace and type nodes into the index file as needed
586 string ns = DocUtils.GetNamespace (type);
587 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode ("Namespace[@Name='" + ns + "']");
588 if (nsnode == null) {
589 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
590 nsnode.SetAttribute ("Name", ns);
591 index_types.AppendChild (nsnode);
593 string doc_typename = GetDocTypeName (type);
594 XmlElement typenode = (XmlElement) nsnode.SelectSingleNode ("Type[@Name='" + typename + "']");
595 if (typenode == null) {
596 typenode = index_types.OwnerDocument.CreateElement ("Type");
597 typenode.SetAttribute ("Name", typename);
598 nsnode.AppendChild (typenode);
600 if (typename != doc_typename)
601 typenode.SetAttribute("DisplayName", doc_typename);
603 typenode.RemoveAttribute("DisplayName");
605 typenode.SetAttribute ("Kind", GetTypeKind (type));
608 private void DoUpdateAssemblies (string source, string dest)
610 string indexfile = dest + "/index.xml";
612 if (System.IO.File.Exists(indexfile)) {
613 index = new XmlDocument();
614 index.Load(indexfile);
617 ClearElement(index.DocumentElement, "Assembly");
618 ClearElement(index.DocumentElement, "Attributes");
620 index = CreateIndexStub();
623 string defaultTitle = "Untitled";
624 if (assemblies.Count == 1)
625 defaultTitle = assemblies[0].Name.Name;
626 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
628 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
629 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
630 index_assemblies.RemoveAll ();
633 HashSet<string> goodfiles = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
635 foreach (AssemblyDefinition assm in assemblies) {
636 AddIndexAssembly (assm, index_assemblies);
637 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
640 SortIndexEntries (index_types);
642 CleanupFiles (dest, goodfiles);
643 CleanupIndexTypes (index_types, goodfiles);
644 CleanupExtensions (index_types);
646 WriteFile (indexfile, FileMode.Create,
647 writer => WriteXml(index.DocumentElement, writer));
650 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
652 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
654 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
655 string typename = GetTypeFileName(type);
656 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0 || forwardedTypes.Contains (type.FullName))
659 string reltypepath = DoUpdateType (type, source, dest);
660 if (reltypepath == null)
663 // Add namespace and type nodes into the index file as needed
664 AddIndexType (type, index_types);
666 // Ensure the namespace index file exists
667 string onsdoc = DocUtils.PathCombine (dest, type.Namespace + ".xml");
668 string nsdoc = DocUtils.PathCombine (dest, "ns-" + type.Namespace + ".xml");
669 if (File.Exists (onsdoc)) {
670 File.Move (onsdoc, nsdoc);
673 if (!File.Exists (nsdoc)) {
674 Console.WriteLine("New Namespace File: " + type.Namespace);
675 WriteNamespaceStub(type.Namespace, dest);
678 goodfiles.Add (reltypepath);
682 private static void SortIndexEntries (XmlElement indexTypes)
684 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
685 XmlNodeComparer c = new AttributeNameComparer ();
686 SortXmlNodes (indexTypes, namespaces, c);
688 for (int i = 0; i < namespaces.Count; ++i)
689 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
692 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
694 MyXmlNodeList l = new MyXmlNodeList (children.Count);
695 for (int i = 0; i < children.Count; ++i)
696 l.Add (children [i]);
698 for (int i = l.Count - 1; i > 0; --i) {
699 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
703 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
705 public abstract int Compare (XmlNode x, XmlNode y);
707 public int Compare (object x, object y)
709 return Compare ((XmlNode) x, (XmlNode) y);
713 class AttributeNameComparer : XmlNodeComparer {
716 public AttributeNameComparer ()
721 public AttributeNameComparer (string attribute)
723 this.attribute = attribute;
726 public override int Compare (XmlNode x, XmlNode y)
728 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
732 class VersionComparer : XmlNodeComparer {
733 public override int Compare (XmlNode x, XmlNode y)
735 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
736 string a = GetVersion (x.InnerText);
737 string b = GetVersion (y.InnerText);
738 return new Version (a).CompareTo (new Version (b));
741 static string GetVersion (string v)
743 int n = v.IndexOf ("x");
746 return v.Substring (0, n-1);
750 private static string GetTypeKind (TypeDefinition type)
753 return "Enumeration";
754 if (type.IsValueType)
756 if (type.IsInterface)
758 if (DocUtils.IsDelegate (type))
760 if (type.IsClass || type.FullName == "System.Enum") // FIXME
762 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
765 private static bool IsPublic (TypeDefinition type)
767 TypeDefinition decl = type;
768 while (decl != null) {
769 if (!(decl.IsPublic || decl.IsNestedPublic ||
770 decl.IsNestedFamily || decl.IsNestedFamily || decl.IsNestedFamilyOrAssembly)) {
773 decl = (TypeDefinition) decl.DeclaringType;
778 private void CleanupFiles (string dest, HashSet<string> goodfiles)
780 // Look for files that no longer correspond to types
781 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
782 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
783 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
784 if (!goodfiles.Contains (relTypeFile)) {
785 XmlDocument doc = new XmlDocument ();
786 doc.Load (typefile.FullName);
787 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
788 if (e != null && !no_assembly_versions && UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
789 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
790 WriteXml(doc.DocumentElement, writer);
791 goodfiles.Add (relTypeFile);
794 string newname = typefile.FullName + ".remove";
795 try { System.IO.File.Delete(newname); } catch (Exception) { }
796 try { typefile.MoveTo(newname); } catch (Exception) { }
797 Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
803 private static TextWriter OpenWrite (string path, FileMode mode)
805 var w = new StreamWriter (
806 new FileStream (path, mode),
807 new UTF8Encoding (false)
813 private string[] GetAssemblyVersions ()
815 return (from a in assemblies select GetAssemblyVersion (a)).ToArray ();
818 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
820 // Look for type nodes that no longer correspond to types
821 MyXmlNodeList remove = new MyXmlNodeList ();
822 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
823 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
824 if (!goodfiles.Contains (fulltypename)) {
825 remove.Add (typenode);
828 foreach (XmlNode n in remove)
829 n.ParentNode.RemoveChild (n);
832 private void CleanupExtensions (XmlElement index_types)
834 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
835 if (extensionMethods.Count == 0) {
838 index_types.SelectSingleNode ("/Overview").RemoveChild (e);
842 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
843 index_types.SelectSingleNode ("/Overview").AppendChild (e);
847 extensionMethods.Sort (DefaultExtensionMethodComparer);
848 foreach (XmlNode m in extensionMethods) {
849 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
853 class ExtensionMethodComparer : XmlNodeComparer {
854 public override int Compare (XmlNode x, XmlNode y)
856 XmlNode xLink = x.SelectSingleNode ("Member/Link");
857 XmlNode yLink = y.SelectSingleNode ("Member/Link");
859 int n = xLink.Attributes ["Type"].Value.CompareTo (
860 yLink.Attributes ["Type"].Value);
863 n = xLink.Attributes ["Member"].Value.CompareTo (
864 yLink.Attributes ["Member"].Value);
865 if (n == 0 && !object.ReferenceEquals (x, y))
866 throw new InvalidOperationException ("Duplicate extension method found!");
871 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
873 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince)
875 Console.WriteLine(message + ": " + type.FullName);
877 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
879 // Update type metadata
880 UpdateType(basefile.DocumentElement, type);
882 // Update existing members. Delete member nodes that no longer should be there,
883 // and remember what members are already documented so we don't add them again.
885 MyXmlNodeList todelete = new MyXmlNodeList ();
886 foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
887 XmlElement oldmember = info.Node;
888 MemberReference oldmember2 = info.Member;
889 string sig = oldmember2 != null ? memberFormatters [0].GetDeclaration (oldmember2) : null;
891 // Interface implementations and overrides are deleted from the docs
892 // unless the overrides option is given.
893 if (oldmember2 != null && sig == null)
896 // Deleted (or signature changed)
897 if (oldmember2 == null) {
898 if (!no_assembly_versions && UpdateAssemblyVersions (oldmember, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
900 DeleteMember ("Member Removed", output, oldmember, todelete);
905 if (seenmembers.ContainsKey (sig)) {
906 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
907 // ignore, already seen
909 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
910 DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
912 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
916 // Update signature information
919 seenmembers.Add (sig, oldmember);
921 foreach (XmlElement oldmember in todelete)
922 oldmember.ParentNode.RemoveChild (oldmember);
925 if (!DocUtils.IsDelegate (type)) {
926 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
927 foreach (MemberReference m in type.GetMembers()) {
928 if (m is TypeDefinition) continue;
930 string sig = memberFormatters [0].GetDeclaration (m);
931 if (sig == null) continue;
932 if (seenmembers.ContainsKey(sig)) continue;
934 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
935 if (mm == null) continue;
936 members.AppendChild( mm );
938 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
943 // Import code snippets from files
944 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
945 if (!(code is XmlElement)) continue;
946 string file = ((XmlElement)code).GetAttribute("src");
947 string lang = ((XmlElement)code).GetAttribute("lang");
949 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
951 code.InnerText = src;
955 if (insertSince && since != null) {
956 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
957 docs.AppendChild (CreateSinceNode (basefile));
961 XmlElement d = basefile.DocumentElement ["Docs"];
962 XmlElement m = basefile.DocumentElement ["Members"];
963 if (d != null && m != null)
964 basefile.DocumentElement.InsertBefore (
965 basefile.DocumentElement.RemoveChild (d), m);
970 WriteXml(basefile.DocumentElement, Console.Out);
972 FileInfo file = new FileInfo (output);
973 if (!file.Directory.Exists) {
974 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
975 file.Directory.Create ();
977 WriteFile (output, FileMode.Create,
978 writer => WriteXml(basefile.DocumentElement, writer));
982 private string GetCodeSource (string lang, string file)
985 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
986 // Grab the specified region
987 string region = "#region " + file.Substring (anchorStart + 4);
988 file = file.Substring (0, anchorStart + 3);
990 using (StreamReader reader = new StreamReader (file)) {
992 StringBuilder src = new StringBuilder ();
994 while ((line = reader.ReadLine ()) != null) {
995 if (line.Trim() == region) {
996 indent = line.IndexOf (region);
999 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
1004 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
1007 return src.ToString ();
1009 } catch (Exception e) {
1010 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
1011 file, region, show_exceptions ? e.ToString () : e.Message);
1016 using (StreamReader reader = new StreamReader (file))
1017 return reader.ReadToEnd ();
1018 } catch (Exception e) {
1019 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
1024 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
1026 string format = output != null
1027 ? "{0}: File='{1}'; Signature='{4}'"
1028 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1032 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1033 member.Attributes ["MemberName"].Value,
1034 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
1035 if (!delete && MemberDocsHaveUserContent (member)) {
1036 Warning ("Member deletions must be enabled with the --delete option.");
1038 todelete.Add (member);
1043 class MemberComparer : XmlNodeComparer {
1044 public override int Compare (XmlNode x, XmlNode y)
1047 string xMemberName = x.Attributes ["MemberName"].Value;
1048 string yMemberName = y.Attributes ["MemberName"].Value;
1050 // generic methods *end* with '>'
1051 // it's possible for explicitly implemented generic interfaces to
1052 // contain <...> without being a generic method
1053 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1054 (r = xMemberName.CompareTo (yMemberName)) != 0)
1058 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1059 xMemberName = xMemberName.Substring (0, lt);
1060 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1061 yMemberName = yMemberName.Substring (0, lt);
1062 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1065 // if @MemberName matches, then it's either two different types of
1066 // members sharing the same name, e.g. field & property, or it's an
1067 // overloaded method.
1068 // for different type, sort based on MemberType value.
1069 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1070 y.SelectSingleNode ("MemberType").InnerText);
1074 // same type -- must be an overloaded method. Sort based on type
1075 // parameter count, then parameter count, then by the parameter
1077 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1078 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1079 if (xTypeParams.Count != yTypeParams.Count)
1080 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1081 for (int i = 0; i < xTypeParams.Count; ++i) {
1082 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1083 yTypeParams [i].Attributes ["Name"].Value);
1088 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1089 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1090 if (xParams.Count != yParams.Count)
1091 return xParams.Count <= yParams.Count ? -1 : 1;
1092 for (int i = 0; i < xParams.Count; ++i) {
1093 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1094 yParams [i].Attributes ["Type"].Value);
1098 // all parameters match, but return value might not match if it was
1099 // changed between one version and another.
1100 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1101 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1102 if (xReturn != null && yReturn != null) {
1103 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1112 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1114 private static void SortTypeMembers (XmlNode members)
1116 if (members == null)
1118 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1121 private static bool MemberDocsHaveUserContent (XmlNode e)
1123 e = (XmlElement)e.SelectSingleNode("Docs");
1124 if (e == null) return false;
1125 foreach (XmlElement d in e.SelectNodes("*"))
1126 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1131 // UPDATE HELPER FUNCTIONS
1133 // CREATE A STUB DOCUMENTATION FILE
1135 public XmlElement StubType (TypeDefinition type, string output)
1137 string typesig = typeFormatters [0].GetDeclaration (type);
1138 if (typesig == null) return null; // not publicly visible
1140 XmlDocument doc = new XmlDocument();
1141 XmlElement root = doc.CreateElement("Type");
1142 doc.AppendChild (root);
1144 DoUpdateType2 ("New Type", doc, type, output, true);
1149 private XmlElement CreateSinceNode (XmlDocument doc)
1151 XmlElement s = doc.CreateElement ("since");
1152 s.SetAttribute ("version", since);
1156 // STUBBING/UPDATING FUNCTIONS
1158 public void UpdateType (XmlElement root, TypeDefinition type)
1160 root.SetAttribute("Name", GetDocTypeName (type));
1161 root.SetAttribute("FullName", GetDocTypeFullName (type));
1163 foreach (MemberFormatter f in typeFormatters) {
1164 string element = "TypeSignature[@Language='" + f.Language + "']";
1165 WriteElementAttribute (root, element, "Language", f.Language);
1166 WriteElementAttribute (root, element, "Value", f.GetDeclaration (type));
1169 XmlElement ass = WriteElement(root, "AssemblyInfo");
1170 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1171 if (!no_assembly_versions) {
1172 UpdateAssemblyVersions (root, type, true);
1175 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1176 foreach (var version in versions)
1177 ass.RemoveChild (version);
1179 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1180 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1182 ClearElement(ass, "AssemblyCulture");
1184 // Why-oh-why do we put assembly attributes in each type file?
1185 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1186 // since they're outdated in current docs, and a waste of space.
1187 //MakeAttributes(ass, type.Assembly, true);
1188 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1189 if (assattrs != null)
1190 ass.RemoveChild(assattrs);
1192 NormalizeWhitespace(ass);
1194 if (type.IsGenericType ()) {
1195 MakeTypeParameters (root, type.GenericParameters);
1197 ClearElement(root, "TypeParameters");
1200 if (type.BaseType != null) {
1201 XmlElement basenode = WriteElement(root, "Base");
1203 string basetypename = GetDocTypeFullName (type.BaseType);
1204 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1205 WriteElementText(root, "Base/BaseTypeName", basetypename);
1207 // Document how this type instantiates the generic parameters of its base type
1208 TypeReference origBase = type.BaseType.GetElementType ();
1209 if (origBase.IsGenericType ()) {
1210 ClearElement(basenode, "BaseTypeArguments");
1211 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1212 IList<TypeReference> baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1213 IList<GenericParameter> baseGenParams = origBase.GenericParameters;
1214 if (baseGenArgs.Count != baseGenParams.Count)
1215 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1216 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1217 GenericParameter param = baseGenParams [i];
1218 TypeReference value = baseGenArgs [i];
1220 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1221 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1222 bta.AppendChild(arg);
1223 arg.SetAttribute ("TypeParamName", param.Name);
1224 arg.InnerText = GetDocTypeFullName (value);
1228 ClearElement(root, "Base");
1231 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1232 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1233 List<string> interface_names = userInterfaces
1234 .Select (iface => GetDocTypeFullName (iface))
1238 XmlElement interfaces = WriteElement(root, "Interfaces");
1239 interfaces.RemoveAll();
1240 foreach (string iname in interface_names) {
1241 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1242 interfaces.AppendChild(iface);
1243 WriteElementText(iface, "InterfaceName", iname);
1246 ClearElement(root, "Interfaces");
1249 MakeAttributes (root, GetCustomAttributes (type));
1251 if (DocUtils.IsDelegate (type)) {
1252 MakeTypeParameters (root, type.GenericParameters);
1253 MakeParameters(root, type.GetMethod("Invoke").Parameters);
1254 MakeReturnValue(root, type.GetMethod("Invoke"));
1257 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1258 MakeDocNode (typeInfo);
1260 if (!DocUtils.IsDelegate (type))
1261 WriteElement (root, "Members");
1263 OrderTypeNodes (root, root.ChildNodes);
1264 NormalizeWhitespace(root);
1267 static readonly string[] TypeNodeOrder = {
1271 "ThreadingSafetyStatement",
1272 "ThreadSafetyStatement",
1284 static void OrderTypeNodes (XmlNode member, XmlNodeList children)
1286 ReorderNodes (member, children, TypeNodeOrder);
1289 internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1291 List<T> l = new List<T> (list);
1296 private void UpdateMember (DocsNodeInfo info)
1298 XmlElement me = (XmlElement) info.Node;
1299 MemberReference mi = info.Member;
1301 foreach (MemberFormatter f in memberFormatters) {
1302 string element = "MemberSignature[@Language='" + f.Language + "']";
1303 WriteElementAttribute (me, element, "Language", f.Language);
1304 WriteElementAttribute (me, element, "Value", f.GetDeclaration (mi));
1307 WriteElementText(me, "MemberType", GetMemberType(mi));
1309 if (!no_assembly_versions) {
1310 UpdateAssemblyVersions (me, mi, true);
1313 ClearElement (me, "AssemblyInfo");
1316 MakeAttributes (me, GetCustomAttributes (mi));
1318 MakeReturnValue(me, mi);
1319 if (mi is MethodReference) {
1320 MethodReference mb = (MethodReference) mi;
1321 if (mb.IsGenericMethod ())
1322 MakeTypeParameters (me, mb.GenericParameters);
1324 MakeParameters(me, mi);
1327 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1328 WriteElementText(me, "MemberValue", fieldValue);
1330 info.Node = WriteElement (me, "Docs");
1332 OrderMemberNodes (me, me.ChildNodes);
1333 UpdateExtensionMethods (me, info);
1336 static readonly string[] MemberNodeOrder = {
1351 static void OrderMemberNodes (XmlNode member, XmlNodeList children)
1353 ReorderNodes (member, children, MemberNodeOrder);
1356 static void ReorderNodes (XmlNode node, XmlNodeList children, string[] ordering)
1358 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1359 for (int i = 0; i < ordering.Length; ++i) {
1360 for (int j = 0; j < children.Count; ++j) {
1361 XmlNode c = children [j];
1362 if (c.Name == ordering [i]) {
1363 newChildren.Add (c);
1367 if (newChildren.Count >= 0)
1368 node.PrependChild ((XmlNode) newChildren [0]);
1369 for (int i = 1; i < newChildren.Count; ++i) {
1370 XmlNode prev = (XmlNode) newChildren [i-1];
1371 XmlNode cur = (XmlNode) newChildren [i];
1372 node.RemoveChild (cur);
1373 node.InsertAfter (cur, prev);
1377 IEnumerable<string> GetCustomAttributes (MemberReference mi)
1379 IEnumerable<string> attrs = Enumerable.Empty<string>();
1381 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1383 attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1385 PropertyDefinition pd = mi as PropertyDefinition;
1387 if (pd.GetMethod != null)
1388 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1389 if (pd.SetMethod != null)
1390 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1393 EventDefinition ed = mi as EventDefinition;
1395 if (ed.AddMethod != null)
1396 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1397 if (ed.RemoveMethod != null)
1398 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1404 IEnumerable<string> GetCustomAttributes (IList<CustomAttribute> attributes, string prefix)
1406 foreach (CustomAttribute attribute in attributes.OrderBy (ca => ca.AttributeType.FullName)) {
1408 TypeDefinition attrType = attribute.AttributeType as TypeDefinition;
1409 if (attrType != null && !IsPublic (attrType))
1411 if (slashdocFormatter.GetName (attribute.AttributeType) == null)
1414 if (Array.IndexOf (IgnorableAttributes, attribute.AttributeType.FullName) >= 0)
1417 StringList fields = new StringList ();
1419 for (int i = 0; i < attribute.ConstructorArguments.Count; ++i) {
1420 CustomAttributeArgument argument = attribute.ConstructorArguments [i];
1421 fields.Add (MakeAttributesValueString (
1426 (from namedArg in attribute.Fields
1427 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value })
1429 (from namedArg in attribute.Properties
1430 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value }))
1431 .OrderBy (v => v.Name);
1432 foreach (var d in namedArgs)
1433 fields.Add (string.Format ("{0}={1}", d.Name,
1434 MakeAttributesValueString (d.Value, d.Type)));
1436 string a2 = String.Join(", ", fields.ToArray ());
1437 if (a2 != "") a2 = "(" + a2 + ")";
1439 string name = attribute.GetDeclaringType();
1440 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1441 yield return prefix + name + a2;
1445 static readonly string[] ValidExtensionMembers = {
1454 static readonly string[] ValidExtensionDocMembers = {
1460 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1462 MethodDefinition me = info.Member as MethodDefinition;
1465 if (info.Parameters.Count < 1)
1467 if (!DocUtils.IsExtensionMethod (me))
1470 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1471 XmlNode member = e.CloneNode (true);
1472 em.AppendChild (member);
1473 RemoveExcept (member, ValidExtensionMembers);
1474 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1475 WriteElementText (member, "MemberType", "ExtensionMethod");
1476 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1477 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1478 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1479 member.AppendChild (link);
1480 AddTargets (em, info);
1482 extensionMethods.Add (em);
1485 private static void RemoveExcept (XmlNode node, string[] except)
1489 MyXmlNodeList remove = null;
1490 foreach (XmlNode n in node.ChildNodes) {
1491 if (Array.BinarySearch (except, n.Name) < 0) {
1493 remove = new MyXmlNodeList ();
1498 foreach (XmlNode n in remove)
1499 node.RemoveChild (n);
1502 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1504 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1505 member.PrependChild (targets);
1506 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1507 AppendElementAttributeText (targets, "Target", "Type",
1508 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1511 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1512 IList<TypeReference> constraints = gp.Constraints;
1513 if (constraints.Count == 0)
1514 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1516 foreach (TypeReference c in constraints)
1517 AppendElementAttributeText(targets, "Target", "Type",
1518 slashdocFormatter.GetDeclaration (c));
1522 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1525 TypeDefinition type = field.DeclaringType.Resolve ();
1526 if (type != null && type.IsEnum) return false;
1528 if (type != null && type.IsGenericType ()) return false;
1529 if (!field.HasConstant)
1531 if (field.IsLiteral) {
1532 object val = field.Constant;
1533 if (val == null) value = "null";
1534 else if (val is Enum) value = val.ToString();
1535 else if (val is IFormattable) {
1536 value = ((IFormattable)val).ToString();
1538 value = "\"" + value + "\"";
1540 if (value != null && value != "")
1546 // XML HELPER FUNCTIONS
1548 internal static XmlElement WriteElement(XmlNode parent, string element) {
1549 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1551 string[] path = element.Split('/');
1552 foreach (string p in path) {
1553 ret = (XmlElement)parent.SelectSingleNode(p);
1556 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1557 ename = ename.Substring(0, ename.IndexOf('['));
1558 ret = parent.OwnerDocument.CreateElement(ename);
1559 parent.AppendChild(ret);
1568 private static void WriteElementText(XmlNode parent, string element, string value) {
1569 XmlElement node = WriteElement(parent, element);
1570 node.InnerText = value;
1573 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1575 XmlElement n = parent.OwnerDocument.CreateElement (element);
1576 parent.AppendChild (n);
1577 n.InnerText = value;
1581 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1583 XmlElement n = parent.OwnerDocument.CreateElement (element);
1584 parent.AppendChild (n);
1585 n.SetAttribute (attribute, value);
1589 internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
1591 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1592 dest.AppendChild (copy);
1596 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1597 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1600 node = WriteElement(parent, element);
1601 node.InnerText = value;
1603 private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
1604 XmlElement node = WriteElement(parent, element);
1605 if (node.GetAttribute(attribute) == value) return;
1606 node.SetAttribute(attribute, value);
1608 internal static void ClearElement(XmlElement parent, string name) {
1609 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1611 parent.RemoveChild(node);
1614 // DOCUMENTATION HELPER FUNCTIONS
1616 private void MakeDocNode (DocsNodeInfo info)
1618 List<GenericParameter> genericParams = info.GenericParameters;
1619 IList<ParameterDefinition> parameters = info.Parameters;
1620 TypeReference returntype = info.ReturnType;
1621 bool returnisreturn = info.ReturnIsReturn;
1622 XmlElement e = info.Node;
1623 bool addremarks = info.AddRemarks;
1625 WriteElementInitialText(e, "summary", "To be added.");
1627 if (parameters != null) {
1628 string[] values = new string [parameters.Count];
1629 for (int i = 0; i < values.Length; ++i)
1630 values [i] = parameters [i].Name;
1631 UpdateParameters (e, "param", values);
1634 if (genericParams != null) {
1635 string[] values = new string [genericParams.Count];
1636 for (int i = 0; i < values.Length; ++i)
1637 values [i] = genericParams [i].Name;
1638 UpdateParameters (e, "typeparam", values);
1641 string retnodename = null;
1642 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
1643 retnodename = returnisreturn ? "returns" : "value";
1644 string retnodename_other = !returnisreturn ? "returns" : "value";
1646 // If it has a returns node instead of a value node, change its name.
1647 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1648 if (retother != null) {
1649 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1650 foreach (XmlNode node in retother)
1651 retnode.AppendChild(node.CloneNode(true));
1652 e.ReplaceChild(retnode, retother);
1654 WriteElementInitialText(e, retnodename, "To be added.");
1657 ClearElement(e, "returns");
1658 ClearElement(e, "value");
1662 WriteElementInitialText(e, "remarks", "To be added.");
1664 if (exceptions.HasValue && info.Member != null &&
1665 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
1666 UpdateExceptions (e, info.Member);
1669 foreach (DocumentationImporter importer in importers)
1670 importer.ImportDocumentation (info);
1672 OrderDocsNodes (e, e.ChildNodes);
1673 NormalizeWhitespace(e);
1676 static readonly string[] DocsNodeOrder = {
1677 "typeparam", "param", "summary", "returns", "value", "remarks",
1680 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
1682 ReorderNodes (docs, children, DocsNodeOrder);
1686 private void UpdateParameters (XmlElement e, string element, string[] values)
1688 if (values != null) {
1689 XmlNode[] paramnodes = new XmlNode[values.Length];
1691 // Some documentation had param nodes with leading spaces.
1692 foreach (XmlElement paramnode in e.SelectNodes(element)){
1693 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
1696 // If a member has only one parameter, we can track changes to
1697 // the name of the parameter easily.
1698 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
1699 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
1702 bool reinsert = false;
1704 // Pick out existing and still-valid param nodes, and
1705 // create nodes for parameters not in the file.
1706 Hashtable seenParams = new Hashtable();
1707 for (int pi = 0; pi < values.Length; pi++) {
1708 string p = values [pi];
1711 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
1712 if (paramnodes[pi] != null) continue;
1714 XmlElement pe = e.OwnerDocument.CreateElement(element);
1715 pe.SetAttribute("name", p);
1716 pe.InnerText = "To be added.";
1717 paramnodes[pi] = pe;
1721 // Remove parameters that no longer exist and check all params are in the right order.
1723 MyXmlNodeList todelete = new MyXmlNodeList ();
1724 foreach (XmlElement paramnode in e.SelectNodes(element)) {
1725 string name = paramnode.GetAttribute("name");
1726 if (!seenParams.ContainsKey(name)) {
1727 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1728 Warning ("The following param node can only be deleted if the --delete option is given: ");
1729 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
1731 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
1732 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1736 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
1737 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1738 e.ParentNode.Attributes ["MemberName"].Value,
1741 Warning ("\tValue={0}", paramnode.OuterXml);
1743 todelete.Add (paramnode);
1748 if ((int)seenParams[name] != idx)
1754 foreach (XmlNode n in todelete) {
1755 n.ParentNode.RemoveChild (n);
1758 // Re-insert the parameter nodes at the top of the doc section.
1760 for (int pi = values.Length-1; pi >= 0; pi--)
1761 e.PrependChild(paramnodes[pi]);
1763 // Clear all existing param nodes
1764 foreach (XmlNode paramnode in e.SelectNodes(element)) {
1765 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1766 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
1767 Console.WriteLine(paramnode.OuterXml);
1769 paramnode.ParentNode.RemoveChild(paramnode);
1775 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
1777 string existingName = pe.GetAttribute ("name");
1778 pe.SetAttribute ("name", newName);
1779 if (existingName == newName)
1781 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
1782 if (paramref.GetAttribute ("name").Trim () == existingName)
1783 paramref.SetAttribute ("name", newName);
1786 class CrefComparer : XmlNodeComparer {
1788 public CrefComparer ()
1792 public override int Compare (XmlNode x, XmlNode y)
1794 string xType = x.Attributes ["cref"].Value;
1795 string yType = y.Attributes ["cref"].Value;
1796 string xNamespace = GetNamespace (xType);
1797 string yNamespace = GetNamespace (yType);
1799 int c = xNamespace.CompareTo (yNamespace);
1802 return xType.CompareTo (yType);
1805 static string GetNamespace (string type)
1807 int n = type.LastIndexOf ('.');
1809 return type.Substring (0, n);
1810 return string.Empty;
1814 private void UpdateExceptions (XmlNode docs, MemberReference member)
1816 string indent = new string (' ', 10);
1817 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
1818 string cref = slashdocFormatter.GetDeclaration (source.Exception);
1819 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
1822 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
1823 e.SetAttribute ("cref", cref);
1824 e.InnerXml = "To be added; from:\n" + indent + "<see cref=\"" +
1825 string.Join ("\" />,\n" + indent + "<see cref=\"",
1826 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
1827 .OrderBy (s => s)) +
1829 docs.AppendChild (e);
1831 SortXmlNodes (docs, docs.SelectNodes ("exception"),
1832 new CrefComparer ());
1835 private static void NormalizeWhitespace(XmlElement e) {
1836 // Remove all text and whitespace nodes from the element so it
1837 // is outputted with nice indentation and no blank lines.
1838 ArrayList deleteNodes = new ArrayList();
1839 foreach (XmlNode n in e)
1840 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
1842 foreach (XmlNode n in deleteNodes)
1843 n.ParentNode.RemoveChild(n);
1846 private static bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
1848 TypeDefinition type = member as TypeDefinition;
1850 type = member.DeclaringType as TypeDefinition;
1851 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
1854 private static string GetAssemblyVersion (AssemblyDefinition assembly)
1856 return assembly.Name.Version.ToString();
1859 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
1861 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
1863 e = root.OwnerDocument.CreateElement("AssemblyInfo");
1864 root.AppendChild(e);
1866 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
1867 .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
1869 // matches.Count > 0 && add: ignore -- already present
1870 if (matches.Count > 0 && !add) {
1871 foreach (XmlNode c in matches)
1874 else if (matches.Count == 0 && add) {
1875 foreach (string sv in assemblyVersions) {
1876 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
1881 // matches.Count == 0 && !add: ignore -- already not present
1883 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
1884 SortXmlNodes (e, avs, new VersionComparer ());
1886 return avs.Count != 0;
1889 // FIXME: get TypeReferences instead of string comparison?
1890 private static string[] IgnorableAttributes = {
1891 // Security related attributes
1892 "System.Reflection.AssemblyKeyFileAttribute",
1893 "System.Reflection.AssemblyDelaySignAttribute",
1894 // Present in @RefType
1895 "System.Runtime.InteropServices.OutAttribute",
1896 // For naming the indexer to use when not using indexers
1897 "System.Reflection.DefaultMemberAttribute",
1898 // for decimal constants
1899 "System.Runtime.CompilerServices.DecimalConstantAttribute",
1900 // compiler generated code
1901 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
1902 // more compiler generated code, e.g. iterator methods
1903 "System.Diagnostics.DebuggerHiddenAttribute",
1904 "System.Runtime.CompilerServices.FixedBufferAttribute",
1905 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
1906 // extension methods
1907 "System.Runtime.CompilerServices.ExtensionAttribute",
1908 // Used to differentiate 'object' from C#4 'dynamic'
1909 "System.Runtime.CompilerServices.DynamicAttribute",
1912 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes)
1914 if (!attributes.Any ()) {
1915 ClearElement (root, "Attributes");
1919 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
1923 e = root.OwnerDocument.CreateElement("Attributes");
1925 foreach (string attribute in attributes) {
1926 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
1929 WriteElementText(ae, "AttributeName", attribute);
1932 if (e.ParentNode == null)
1933 root.AppendChild(e);
1935 NormalizeWhitespace(e);
1938 public static string MakeAttributesValueString (object v, TypeReference valueType)
1942 if (valueType.FullName == "System.Type")
1943 return "typeof(" + v.ToString () + ")";
1944 if (valueType.FullName == "System.String")
1945 return "\"" + v.ToString () + "\"";
1946 if (valueType.FullName == "System.Char")
1947 return "'" + v.ToString () + "'";
1949 return (bool)v ? "true" : "false";
1950 TypeDefinition valueDef = valueType.Resolve ();
1951 if (valueDef == null || !valueDef.IsEnum)
1952 return v.ToString ();
1953 string typename = GetDocTypeFullName (valueType);
1954 var values = GetEnumerationValues (valueDef);
1955 long c = ToInt64 (v);
1956 if (values.ContainsKey (c))
1957 return typename + "." + values [c];
1958 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
1959 return string.Join (" | ",
1960 (from i in values.Keys
1962 select typename + "." + values [i])
1963 .DefaultIfEmpty (v.ToString ()).ToArray ());
1965 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
1968 private static Dictionary<long, string> GetEnumerationValues (TypeDefinition type)
1970 var values = new Dictionary<long, string> ();
1972 (from f in type.Fields
1973 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
1975 values [ToInt64 (f.Constant)] = f.Name;
1980 static long ToInt64 (object value)
1983 return (long) (ulong) value;
1984 return Convert.ToInt64 (value);
1987 private void MakeParameters (XmlElement root, IList<ParameterDefinition> parameters)
1989 XmlElement e = WriteElement(root, "Parameters");
1991 foreach (ParameterDefinition p in parameters) {
1992 XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
1994 pe.SetAttribute("Name", p.Name);
1995 pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
1996 if (p.ParameterType is ByReferenceType) {
1997 if (p.IsOut) pe.SetAttribute("RefType", "out");
1998 else pe.SetAttribute("RefType", "ref");
2000 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
2004 private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams)
2006 if (typeParams == null || typeParams.Count == 0) {
2007 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2009 root.RemoveChild (f);
2012 XmlElement e = WriteElement(root, "TypeParameters");
2014 foreach (GenericParameter t in typeParams) {
2015 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2017 pe.SetAttribute("Name", t.Name);
2018 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""));
2019 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2020 IList<TypeReference> constraints = t.Constraints;
2021 GenericParameterAttributes attrs = t.Attributes;
2022 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2030 ce = root.OwnerDocument.CreateElement ("Constraints");
2032 pe.AppendChild (ce);
2033 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2034 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2035 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2036 AppendElementText (ce, "ParameterAttribute", "Covariant");
2037 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2038 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2039 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2040 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2041 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2042 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2043 foreach (TypeReference c in constraints) {
2044 TypeDefinition cd = c.Resolve ();
2045 AppendElementText (ce,
2046 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2047 GetDocTypeFullName (c));
2052 private void MakeParameters (XmlElement root, MemberReference mi)
2054 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2055 MakeParameters (root, ((MethodDefinition)mi).Parameters);
2056 else if (mi is MethodDefinition) {
2057 MethodDefinition mb = (MethodDefinition) mi;
2058 IList<ParameterDefinition> parameters = mb.Parameters;
2059 MakeParameters(root, parameters);
2060 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2061 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2062 p.SetAttribute ("RefType", "this");
2065 else if (mi is PropertyDefinition) {
2066 IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
2067 if (parameters.Count > 0)
2068 MakeParameters(root, parameters);
2072 else if (mi is FieldDefinition) return;
2073 else if (mi is EventDefinition) return;
2074 else throw new ArgumentException();
2077 internal static string GetDocParameterType (TypeReference type)
2079 return GetDocTypeFullName (type).Replace ("@", "&");
2082 private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes)
2084 XmlElement e = WriteElement(root, "ReturnValue");
2086 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2087 if (attributes != null)
2088 MakeAttributes(e, GetCustomAttributes (attributes, ""));
2091 private void MakeReturnValue (XmlElement root, MemberReference mi)
2093 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2095 else if (mi is MethodDefinition)
2096 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes);
2097 else if (mi is PropertyDefinition)
2098 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
2099 else if (mi is FieldDefinition)
2100 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
2101 else if (mi is EventDefinition)
2102 MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
2104 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2107 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2109 MemberReference mi = info.Member;
2110 if (mi is TypeDefinition) return null;
2112 string sigs = memberFormatters [0].GetDeclaration (mi);
2113 if (sigs == null) return null; // not publicly visible
2115 // no documentation for property/event accessors. Is there a better way of doing this?
2116 if (mi.Name.StartsWith("get_")) return null;
2117 if (mi.Name.StartsWith("set_")) return null;
2118 if (mi.Name.StartsWith("add_")) return null;
2119 if (mi.Name.StartsWith("remove_")) return null;
2120 if (mi.Name.StartsWith("raise_")) return null;
2122 XmlElement me = doc.CreateElement("Member");
2123 me.SetAttribute("MemberName", GetMemberName (mi));
2127 if (exceptions.HasValue &&
2128 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2129 UpdateExceptions (info.Node, info.Member);
2131 if (since != null) {
2132 XmlNode docs = me.SelectSingleNode("Docs");
2133 docs.AppendChild (CreateSinceNode (doc));
2139 internal static string GetMemberName (MemberReference mi)
2141 MethodDefinition mb = mi as MethodDefinition;
2143 PropertyDefinition pi = mi as PropertyDefinition;
2146 return DocUtils.GetPropertyName (pi);
2148 StringBuilder sb = new StringBuilder (mi.Name.Length);
2149 if (!DocUtils.IsExplicitlyImplemented (mb))
2150 sb.Append (mi.Name);
2152 TypeReference iface;
2153 MethodReference ifaceMethod;
2154 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2155 sb.Append (GetDocTypeFullName (iface));
2157 sb.Append (ifaceMethod.Name);
2159 if (mb.IsGenericMethod ()) {
2160 IList<GenericParameter> typeParams = mb.GenericParameters;
2161 if (typeParams.Count > 0) {
2163 sb.Append (typeParams [0].Name);
2164 for (int i = 1; i < typeParams.Count; ++i)
2165 sb.Append (",").Append (typeParams [i].Name);
2169 return sb.ToString ();
2172 /// SIGNATURE GENERATION FUNCTIONS
2173 internal static bool IsPrivate (MemberReference mi)
2175 return memberFormatters [0].GetDeclaration (mi) == null;
2178 internal static string GetMemberType (MemberReference mi)
2180 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2181 return "Constructor";
2182 if (mi is MethodDefinition)
2184 if (mi is PropertyDefinition)
2186 if (mi is FieldDefinition)
2188 if (mi is EventDefinition)
2190 throw new ArgumentException();
2193 private static string GetDocTypeName (TypeReference type)
2195 return docTypeFormatter.GetName (type);
2198 internal static string GetDocTypeFullName (TypeReference type)
2200 return DocTypeFullMemberFormatter.Default.GetName (type);
2203 internal static string GetXPathForMember (DocumentationMember member)
2205 StringBuilder xpath = new StringBuilder ();
2206 xpath.Append ("//Members/Member[@MemberName=\"")
2207 .Append (member.MemberName)
2209 if (member.Parameters != null && member.Parameters.Count > 0) {
2210 xpath.Append ("/Parameters[count(Parameter) = ")
2211 .Append (member.Parameters.Count);
2212 for (int i = 0; i < member.Parameters.Count; ++i) {
2213 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2214 xpath.Append (member.Parameters [i]);
2215 xpath.Append ("\"");
2217 xpath.Append ("]/..");
2219 return xpath.ToString ();
2222 public static string GetXPathForMember (XPathNavigator member)
2224 StringBuilder xpath = new StringBuilder ();
2225 xpath.Append ("//Type[@FullName=\"")
2226 .Append (member.SelectSingleNode ("../../@FullName").Value)
2228 xpath.Append ("Members/Member[@MemberName=\"")
2229 .Append (member.SelectSingleNode ("@MemberName").Value)
2231 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2232 if (parameters.Count > 0) {
2233 xpath.Append ("/Parameters[count(Parameter) = ")
2234 .Append (parameters.Count);
2236 while (parameters.MoveNext ()) {
2238 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2239 xpath.Append (parameters.Current.Value);
2240 xpath.Append ("\"");
2242 xpath.Append ("]/..");
2244 return xpath.ToString ();
2247 public static string GetXPathForMember (MemberReference member)
2249 StringBuilder xpath = new StringBuilder ();
2250 xpath.Append ("//Type[@FullName=\"")
2251 .Append (member.DeclaringType.FullName)
2253 xpath.Append ("Members/Member[@MemberName=\"")
2254 .Append (GetMemberName (member))
2257 IList<ParameterDefinition> parameters = null;
2258 if (member is MethodDefinition)
2259 parameters = ((MethodDefinition) member).Parameters;
2260 else if (member is PropertyDefinition) {
2261 parameters = ((PropertyDefinition) member).Parameters;
2263 if (parameters != null && parameters.Count > 0) {
2264 xpath.Append ("/Parameters[count(Parameter) = ")
2265 .Append (parameters.Count);
2266 for (int i = 0; i < parameters.Count; ++i) {
2267 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2268 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2269 xpath.Append ("\"");
2271 xpath.Append ("]/..");
2273 return xpath.ToString ();
2277 static class CecilExtensions {
2278 public static string GetDeclaringType(this CustomAttribute attribute)
2280 return attribute.Constructor.DeclaringType.FullName;
2283 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
2285 foreach (var c in type.Methods.Where (m => m.IsConstructor))
2286 yield return (MemberReference) c;
2287 foreach (var e in type.Events)
2288 yield return (MemberReference) e;
2289 foreach (var f in type.Fields)
2290 yield return (MemberReference) f;
2291 foreach (var m in type.Methods.Where (m => !m.IsConstructor))
2292 yield return (MemberReference) m;
2293 foreach (var t in type.NestedTypes)
2294 yield return (MemberReference) t;
2295 foreach (var p in type.Properties)
2296 yield return (MemberReference) p;
2299 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
2301 return GetMembers (type).Where (m => m.Name == member);
2304 public static MemberReference GetMember (this TypeDefinition type, string member)
2306 return GetMembers (type, member).EnsureZeroOrOne ();
2309 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2311 if (source.Count () > 1)
2312 throw new InvalidOperationException ("too many matches");
2313 return source.FirstOrDefault ();
2316 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2319 .Where (m => m.Name == method)
2320 .EnsureZeroOrOne ();
2323 public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
2325 TypeDefinition def = type as TypeDefinition;
2327 return new MemberReference [0];
2328 CustomAttribute defMemberAttr = def.CustomAttributes
2329 .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
2330 if (defMemberAttr == null)
2331 return new MemberReference [0];
2332 string name = (string) defMemberAttr.ConstructorArguments [0].Value;
2333 return def.Properties
2334 .Where (p => p.Name == name)
2335 .Select (p => (MemberReference) p);
2338 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2340 return assembly.Modules.SelectMany (md => md.GetAllTypes ());
2343 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2345 return GetTypes (assembly)
2346 .Where (td => td.FullName == type)
2347 .EnsureZeroOrOne ();
2350 public static bool IsGenericType (this TypeReference type)
2352 return type.GenericParameters.Count > 0;
2355 public static bool IsGenericMethod (this MethodReference method)
2357 return method.GenericParameters.Count > 0;
2360 public static MemberReference Resolve (this MemberReference member)
2362 FieldReference fr = member as FieldReference;
2364 return fr.Resolve ();
2365 MethodReference mr = member as MethodReference;
2367 return mr.Resolve ();
2368 TypeReference tr = member as TypeReference;
2370 return tr.Resolve ();
2371 PropertyReference pr = member as PropertyReference;
2374 EventReference er = member as EventReference;
2377 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2380 public static TypeReference GetUnderlyingType (this TypeDefinition type)
2384 return type.Fields.First (f => f.Name == "value__").FieldType;
2387 public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
2389 return self.Types.SelectMany (t => t.GetAllTypes ());
2392 static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
2396 if (!self.HasNestedTypes)
2399 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
2404 static class DocUtils {
2405 public static bool IsExplicitlyImplemented (MethodDefinition method)
2407 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2410 public static string GetTypeDotMember (string name)
2412 int startType, startMethod;
2413 startType = startMethod = -1;
2414 for (int i = 0; i < name.Length; ++i) {
2415 if (name [i] == '.') {
2416 startType = startMethod;
2420 return name.Substring (startType+1);
2423 public static string GetMember (string name)
2425 int i = name.LastIndexOf ('.');
2428 return name.Substring (i+1);
2431 public static void GetInfoForExplicitlyImplementedMethod (
2432 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2436 if (method.Overrides.Count != 1)
2437 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2438 iface = method.Overrides [0].DeclaringType;
2439 ifaceMethod = method.Overrides [0];
2442 public static string GetPropertyName (PropertyDefinition pi)
2444 // Issue: (g)mcs-generated assemblies that explicitly implement
2445 // properties don't specify the full namespace, just the
2446 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2447 MethodDefinition method = pi.GetMethod;
2449 method = pi.SetMethod;
2450 if (!IsExplicitlyImplemented (method))
2453 // Need to determine appropriate namespace for this member.
2454 TypeReference iface;
2455 MethodReference ifaceMethod;
2456 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2457 return string.Join (".", new string[]{
2458 DocTypeFullMemberFormatter.Default.GetName (iface),
2459 GetMember (pi.Name)});
2462 public static string GetNamespace (TypeReference type)
2464 if (type.GetElementType ().IsNested)
2465 type = type.GetElementType ();
2466 while (type != null && type.IsNested)
2467 type = type.DeclaringType;
2469 return string.Empty;
2470 return type.Namespace;
2473 public static string PathCombine (string dir, string path)
2479 return Path.Combine (dir, path);
2482 public static bool IsExtensionMethod (MethodDefinition method)
2485 method.CustomAttributes
2486 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2487 && method.DeclaringType.CustomAttributes
2488 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
2491 public static bool IsDelegate (TypeDefinition type)
2493 TypeReference baseRef = type.BaseType;
2494 if (baseRef == null)
2496 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2497 baseRef.FullName == "System.MulticastDelegate";
2500 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2502 List<TypeReference> decls = new List<TypeReference> ();
2504 while (type.DeclaringType != null) {
2505 decls.Add (type.DeclaringType);
2506 type = type.DeclaringType;
2512 public static int GetGenericArgumentCount (TypeReference type)
2514 GenericInstanceType inst = type as GenericInstanceType;
2516 ? inst.GenericArguments.Count
2517 : type.GenericParameters.Count;
2520 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
2522 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
2523 List<TypeReference> userInterfaces = new List<TypeReference> ();
2524 foreach (TypeReference iface in type.Interfaces) {
2525 TypeReference lookup = iface.Resolve () ?? iface;
2526 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
2527 userInterfaces.Add (iface);
2529 return userInterfaces;
2532 private static string GetQualifiedTypeName (TypeReference type)
2534 return "[" + type.Scope.Name + "]" + type.FullName;
2537 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
2539 HashSet<string> inheritedInterfaces = new HashSet<string> ();
2540 Action<TypeDefinition> a = null;
2542 if (t == null) return;
2543 foreach (TypeReference r in t.Interfaces) {
2544 inheritedInterfaces.Add (GetQualifiedTypeName (r));
2548 TypeReference baseRef = type.BaseType;
2549 while (baseRef != null) {
2550 TypeDefinition baseDef = baseRef.Resolve ();
2551 if (baseDef != null) {
2553 baseRef = baseDef.BaseType;
2558 foreach (TypeReference r in type.Interfaces)
2560 return inheritedInterfaces;
2564 class DocsNodeInfo {
2565 public DocsNodeInfo (XmlElement node)
2570 public DocsNodeInfo (XmlElement node, TypeDefinition type)
2576 public DocsNodeInfo (XmlElement node, MemberReference member)
2579 SetMemberInfo (member);
2582 void SetType (TypeDefinition type)
2585 throw new ArgumentNullException ("type");
2587 GenericParameters = new List<GenericParameter> (type.GenericParameters);
2588 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
2589 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
2590 for (int i = 0; i < declTypes.Count - 1; ++i) {
2591 int remove = System.Math.Min (maxGenArgs,
2592 DocUtils.GetGenericArgumentCount (declTypes [i]));
2593 maxGenArgs -= remove;
2594 while (remove-- > 0)
2595 GenericParameters.RemoveAt (0);
2597 if (DocUtils.IsDelegate (type)) {
2598 Parameters = type.GetMethod("Invoke").Parameters;
2599 ReturnType = type.GetMethod("Invoke").ReturnType;
2600 ReturnIsReturn = true;
2604 void SetMemberInfo (MemberReference member)
2607 throw new ArgumentNullException ("member");
2608 ReturnIsReturn = true;
2612 if (member is MethodReference ) {
2613 MethodReference mr = (MethodReference) member;
2614 Parameters = mr.Parameters;
2615 if (mr.IsGenericMethod ()) {
2616 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
2619 else if (member is PropertyDefinition) {
2620 Parameters = ((PropertyDefinition) member).Parameters;
2623 if (member is MethodDefinition) {
2624 ReturnType = ((MethodDefinition) member).ReturnType;
2625 } else if (member is PropertyDefinition) {
2626 ReturnType = ((PropertyDefinition) member).PropertyType;
2627 ReturnIsReturn = false;
2630 // no remarks section for enum members
2631 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
2635 public TypeReference ReturnType;
2636 public List<GenericParameter> GenericParameters;
2637 public IList<ParameterDefinition> Parameters;
2638 public bool ReturnIsReturn;
2639 public XmlElement Node;
2640 public bool AddRemarks = true;
2641 public MemberReference Member;
2642 public TypeDefinition Type;
2645 class DocumentationEnumerator {
2647 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2649 return GetDocumentationTypes (assembly, forTypes, null);
2652 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2654 foreach (TypeDefinition type in assembly.GetTypes()) {
2655 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
2657 if (seen != null && seen.Contains (type.FullName))
2660 foreach (TypeDefinition nested in type.NestedTypes)
2661 yield return nested;
2665 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2667 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
2668 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
2669 oldmember.RemoveAttribute ("__monodocer-seen__");
2672 MemberReference m = GetMember (type, new DocumentationMember (oldmember));
2674 yield return new DocsNodeInfo (oldmember);
2677 yield return new DocsNodeInfo (oldmember, m);
2682 protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
2684 string membertype = member.MemberType;
2686 string returntype = member.ReturnType;
2688 string docName = member.MemberName;
2689 string[] docTypeParams = GetTypeParameters (docName);
2691 // Loop through all members in this type with the same name
2692 foreach (MemberReference mi in GetReflectionMembers (type, docName)) {
2693 if (mi is TypeDefinition) continue;
2694 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
2696 if (MDocUpdater.IsPrivate (mi))
2699 IList<ParameterDefinition> pis = null;
2700 string[] typeParams = null;
2701 if (mi is MethodDefinition) {
2702 MethodDefinition mb = (MethodDefinition) mi;
2703 pis = mb.Parameters;
2704 if (docTypeParams != null && mb.IsGenericMethod ()) {
2705 IList<GenericParameter> args = mb.GenericParameters;
2706 if (args.Count == docTypeParams.Length) {
2707 typeParams = args.Select (p => p.Name).ToArray ();
2711 else if (mi is PropertyDefinition)
2712 pis = ((PropertyDefinition)mi).Parameters;
2714 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
2715 int pcount = pis == null ? 0 : pis.Count;
2716 if (mcount != pcount)
2719 MethodDefinition mDef = mi as MethodDefinition;
2720 if (mDef != null && !mDef.IsConstructor) {
2721 // Casting operators can overload based on return type.
2722 if (returntype != GetReplacedString (
2723 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
2724 typeParams, docTypeParams)) {
2732 for (int i = 0; i < pis.Count; i++) {
2733 string paramType = GetReplacedString (
2734 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
2735 typeParams, docTypeParams);
2736 if (paramType != (string) member.Parameters [i]) {
2741 if (!good) continue;
2749 static string[] GetTypeParameters (string docName)
2751 if (docName [docName.Length-1] != '>')
2753 StringList types = new StringList ();
2754 int endToken = docName.Length-2;
2755 int i = docName.Length-2;
2757 if (docName [i] == ',' || docName [i] == '<') {
2758 types.Add (docName.Substring (i + 1, endToken - i));
2761 if (docName [i] == '<')
2766 return types.ToArray ();
2769 protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
2771 // need to worry about 4 forms of //@MemberName values:
2772 // 1. "Normal" (non-generic) member names: GetEnumerator
2774 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
2775 // - try as-is, and try type.member (due to "kludge" for property
2777 // 3. "Normal" Generic member names: Sort<T> (CSC)
2778 // - need to remove generic parameters --> "Sort"
2779 // 4. Explicitly-implemented interface members for generic interfaces:
2780 // -- System.Collections.Generic.IEnumerable<T>.Current
2781 // - Try as-is, and try type.member, *keeping* the generic parameters.
2782 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
2783 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
2784 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
2785 // this as (1) or (2).
2786 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
2788 foreach (MemberReference mi in type.GetMembers (docName))
2790 if (CountChars (docName, '.') > 0)
2791 // might be a property; try only type.member instead of
2792 // namespace.type.member.
2793 foreach (MemberReference mi in
2794 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
2801 int startLt, startType, startMethod;
2802 startLt = startType = startMethod = -1;
2803 for (int i = 0; i < docName.Length; ++i) {
2804 switch (docName [i]) {
2813 if (numLt == 0 && (i + 1) < docName.Length)
2814 // there's another character in docName, so this <...> sequence is
2815 // probably part of a generic type -- case 4.
2819 startType = startMethod;
2825 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
2827 foreach (MemberReference mi in type.GetMembers (refName))
2831 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
2834 // If we _still_ haven't found it, we've hit another generic naming issue:
2835 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
2836 // explicitly-implemented METHOD names (not properties), e.g.
2837 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
2838 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
2839 // which the XML docs will contain.
2841 // Alas, we can't derive the Mono name from docName, so we need to iterate
2842 // over all member names, convert them into CSC format, and compare... :-(
2845 foreach (MemberReference mi in type.GetMembers ()) {
2846 if (MDocUpdater.GetMemberName (mi) == docName)
2851 static string GetReplacedString (string typeName, string[] from, string[] to)
2855 for (int i = 0; i < from.Length; ++i)
2856 typeName = typeName.Replace (from [i], to [i]);
2860 private static int CountChars (string s, char c)
2863 for (int i = 0; i < s.Length; ++i) {
2871 class EcmaDocumentationEnumerator : DocumentationEnumerator {
2876 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
2879 this.ecmadocs = ecmaDocs;
2882 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2884 HashSet<string> seen = new HashSet<string> ();
2885 return GetDocumentationTypes (assembly, forTypes, seen)
2886 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
2889 new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2892 while (ecmadocs.Read ()) {
2893 switch (ecmadocs.Name) {
2895 if (typeDepth == -1)
2896 typeDepth = ecmadocs.Depth;
2897 if (ecmadocs.NodeType != XmlNodeType.Element)
2899 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
2901 string typename = ecmadocs.GetAttribute ("FullName");
2902 string typename2 = MDocUpdater.GetTypeFileName (typename);
2903 if (forTypes != null &&
2904 forTypes.BinarySearch (typename) < 0 &&
2905 typename != typename2 &&
2906 forTypes.BinarySearch (typename2) < 0)
2909 if ((t = assembly.GetType (typename)) == null &&
2910 (t = assembly.GetType (typename2)) == null)
2912 seen.Add (typename);
2913 if (typename != typename2)
2914 seen.Add (typename2);
2915 Console.WriteLine (" Import: {0}", t.FullName);
2916 if (ecmadocs.Name != "Docs") {
2917 int depth = ecmadocs.Depth;
2918 while (ecmadocs.Read ()) {
2919 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
2923 if (!ecmadocs.IsStartElement ("Docs"))
2924 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
2934 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2936 return GetMembers (basefile, type)
2937 .Concat (base.GetDocumentationMembers (basefile, type));
2940 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
2942 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
2945 if (ecmadocs.IsEmptyElement)
2948 int membersDepth = ecmadocs.Depth;
2950 while (go && ecmadocs.Read ()) {
2951 switch (ecmadocs.Name) {
2953 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
2955 DocumentationMember dm = new DocumentationMember (ecmadocs);
2956 string xp = MDocUpdater.GetXPathForMember (dm);
2957 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
2959 if (oldmember == null) {
2960 m = GetMember (type, dm);
2962 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2963 type.FullName, dm.MemberSignatures ["C#"]);
2964 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
2967 // oldmember lookup may have failed due to type parameter renames.
2969 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
2970 if (oldmember == null) {
2971 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
2972 oldmember = basefile.CreateElement ("Member");
2973 oldmember.SetAttribute ("MemberName", dm.MemberName);
2974 members.AppendChild (oldmember);
2975 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
2976 XmlElement ms = basefile.CreateElement ("MemberSignature");
2977 ms.SetAttribute ("Language", key);
2978 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
2979 oldmember.AppendChild (ms);
2981 oldmember.SetAttribute ("__monodocer-seen__", "true");
2982 Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
2987 m = GetMember (type, new DocumentationMember (oldmember));
2989 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2990 type.FullName, dm.MemberSignatures ["C#"]);
2993 oldmember.SetAttribute ("__monodocer-seen__", "true");
2995 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
2996 if (ecmadocs.Name != "Docs")
2997 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
3002 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
3011 abstract class DocumentationImporter {
3013 public abstract void ImportDocumentation (DocsNodeInfo info);
3016 class MsxdocDocumentationImporter : DocumentationImporter {
3018 XmlDocument slashdocs;
3020 public MsxdocDocumentationImporter (string file)
3022 var xml = File.ReadAllText (file);
3024 // Ensure Unix line endings
3025 xml = xml.Replace ("\r", "");
3027 slashdocs = new XmlDocument();
3028 slashdocs.LoadXml (xml);
3031 public override void ImportDocumentation (DocsNodeInfo info)
3033 XmlNode elem = GetDocs (info.Member ?? info.Type);
3038 XmlElement e = info.Node;
3040 if (elem.SelectSingleNode("summary") != null)
3041 MDocUpdater.ClearElement(e, "summary");
3042 if (elem.SelectSingleNode("remarks") != null)
3043 MDocUpdater.ClearElement(e, "remarks");
3044 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3045 MDocUpdater.ClearElement(e, "value");
3046 MDocUpdater.ClearElement(e, "returns");
3049 foreach (XmlNode child in elem.ChildNodes) {
3050 switch (child.Name) {
3053 XmlAttribute name = child.Attributes ["name"];
3056 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3058 p2.InnerXml = child.InnerXml;
3061 // Occasionally XML documentation will use <returns/> on
3062 // properties, so let's try to normalize things.
3065 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3066 v.InnerXml = child.InnerXml;
3072 case "permission": {
3073 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3076 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3078 a = e.OwnerDocument.CreateElement (child.Name);
3079 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
3082 a.InnerXml = child.InnerXml;
3086 XmlAttribute cref = child.Attributes ["cref"];
3089 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3091 a = e.OwnerDocument.CreateElement ("altmember");
3092 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
3099 if (child.NodeType == XmlNodeType.Element &&
3100 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3103 MDocUpdater.CopyNode (child, e);
3110 private XmlNode GetDocs (MemberReference member)
3112 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3113 if (slashdocsig != null)
3114 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3119 class EcmaDocumentationImporter : DocumentationImporter {
3123 public EcmaDocumentationImporter (XmlReader ecmaDocs)
3125 this.ecmadocs = ecmaDocs;
3128 public override void ImportDocumentation (DocsNodeInfo info)
3130 if (!ecmadocs.IsStartElement ("Docs")) {
3134 XmlElement e = info.Node;
3136 int depth = ecmadocs.Depth;
3137 ecmadocs.ReadStartElement ("Docs");
3138 while (ecmadocs.Read ()) {
3139 if (ecmadocs.Name == "Docs") {
3140 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3143 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3145 if (!ecmadocs.IsStartElement ())
3147 switch (ecmadocs.Name) {
3150 string name = ecmadocs.GetAttribute ("name");
3153 XmlNode doc = e.SelectSingleNode (
3154 ecmadocs.Name + "[@name='" + name + "']");
3155 string value = ecmadocs.ReadInnerXml ();
3157 doc.InnerXml = value.Replace ("\r", "");
3164 string name = ecmadocs.Name;
3165 string cref = ecmadocs.GetAttribute ("cref");
3168 XmlNode doc = e.SelectSingleNode (
3169 ecmadocs.Name + "[@cref='" + cref + "']");
3170 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3172 doc.InnerXml = value;
3174 XmlElement n = e.OwnerDocument.CreateElement (name);
3175 n.SetAttribute ("cref", cref);
3182 string name = ecmadocs.Name;
3183 string xpath = ecmadocs.Name;
3184 StringList attributes = new StringList (ecmadocs.AttributeCount);
3185 if (ecmadocs.MoveToFirstAttribute ()) {
3187 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3188 } while (ecmadocs.MoveToNextAttribute ());
3189 ecmadocs.MoveToContent ();
3191 if (attributes.Count > 0) {
3192 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3194 XmlNode doc = e.SelectSingleNode (xpath);
3195 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3197 doc.InnerXml = value;
3200 XmlElement n = e.OwnerDocument.CreateElement (name);
3202 foreach (string a in attributes) {
3203 int eq = a.IndexOf ('=');
3204 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3215 class DocumentationMember {
3216 public StringToStringMap MemberSignatures = new StringToStringMap ();
3217 public string ReturnType;
3218 public StringList Parameters;
3219 public string MemberName;
3220 public string MemberType;
3222 public DocumentationMember (XmlReader reader)
3224 MemberName = reader.GetAttribute ("MemberName");
3225 int depth = reader.Depth;
3227 StringList p = new StringList ();
3229 if (reader.NodeType != XmlNodeType.Element)
3231 switch (reader.Name) {
3232 case "MemberSignature":
3233 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3236 MemberType = reader.ReadElementString ();
3239 if (reader.Depth == depth + 2)
3240 ReturnType = reader.ReadElementString ();
3243 if (reader.Depth == depth + 2)
3244 p.Add (reader.GetAttribute ("Type"));
3247 if (reader.Depth == depth + 1)
3251 } while (go && reader.Read () && reader.Depth >= depth);
3257 public DocumentationMember (XmlNode node)
3259 MemberName = node.Attributes ["MemberName"].Value;
3260 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3261 XmlAttribute l = n.Attributes ["Language"];
3262 XmlAttribute v = n.Attributes ["Value"];
3263 if (l != null && v != null)
3264 MemberSignatures [l.Value] = v.Value;
3266 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3267 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
3269 ReturnType = rt.InnerText;
3270 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
3272 Parameters = new StringList (p.Count);
3273 for (int i = 0; i < p.Count; ++i)
3274 Parameters.Add (p [i].Attributes ["Type"].Value);
3279 public class DynamicParserContext {
3280 public ReadOnlyCollection<bool> TransformFlags;
3281 public int TransformIndex;
3283 public DynamicParserContext (ICustomAttributeProvider provider)
3286 if (provider.HasCustomAttributes &&
3287 (da = (provider.CustomAttributes.Cast<CustomAttribute>()
3288 .SingleOrDefault (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))) != null) {
3289 CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
3290 ? new CustomAttributeArgument [0]
3291 : (CustomAttributeArgument[]) da.ConstructorArguments [0].Value;
3293 TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool) t.Value).ToArray());
3298 public enum MemberFormatterState {
3300 WithinGenericTypeParameters,
3303 public abstract class MemberFormatter {
3305 public virtual string Language {
3309 public string GetName (MemberReference member)
3311 return GetName (member, null);
3314 public virtual string GetName (MemberReference member, DynamicParserContext context)
3316 TypeReference type = member as TypeReference;
3318 return GetTypeName (type, context);
3319 MethodReference method = member as MethodReference;
3320 if (method != null && method.Name == ".ctor") // method.IsConstructor
3321 return GetConstructorName (method);
3323 return GetMethodName (method);
3324 PropertyReference prop = member as PropertyReference;
3326 return GetPropertyName (prop);
3327 FieldReference field = member as FieldReference;
3329 return GetFieldName (field);
3330 EventReference e = member as EventReference;
3332 return GetEventName (e);
3333 throw new NotSupportedException ("Can't handle: " +
3334 (member == null ? "null" : member.GetType().ToString()));
3337 protected virtual string GetTypeName (TypeReference type)
3339 return GetTypeName (type, null);
3342 protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
3345 throw new ArgumentNullException ("type");
3346 return _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
3349 protected virtual char[] ArrayDelimeters {
3350 get {return new char[]{'[', ']'};}
3353 protected virtual MemberFormatterState MemberFormatterState { get; set; }
3355 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3357 if (type is ArrayType) {
3358 TypeSpecification spec = type as TypeSpecification;
3359 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
3360 return AppendArrayModifiers (buf, (ArrayType) type);
3362 if (type is ByReferenceType) {
3363 return AppendRefTypeName (buf, type, context);
3365 if (type is PointerType) {
3366 return AppendPointerTypeName (buf, type, context);
3368 AppendNamespace (buf, type);
3369 if (type is GenericParameter) {
3370 return AppendTypeName (buf, type, context);
3372 GenericInstanceType genInst = type as GenericInstanceType;
3373 if (type.GenericParameters.Count == 0 &&
3374 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3375 return AppendFullTypeName (buf, type, context);
3377 return AppendGenericType (buf, type, context);
3380 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3382 string ns = DocUtils.GetNamespace (type);
3383 if (ns != null && ns.Length > 0)
3384 buf.Append (ns).Append ('.');
3388 protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3390 if (type.DeclaringType != null)
3391 AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
3392 return AppendTypeName (buf, type, context);
3395 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3397 if (context != null)
3398 context.TransformIndex++;
3399 return AppendTypeName (buf, type.Name);
3402 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3404 int n = typename.IndexOf ("`");
3406 return buf.Append (typename.Substring (0, n));
3407 return buf.Append (typename);
3410 protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
3412 buf.Append (ArrayDelimeters [0]);
3413 int rank = array.Rank;
3415 buf.Append (new string (',', rank-1));
3416 return buf.Append (ArrayDelimeters [1]);
3419 protected virtual string RefTypeModifier {
3423 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3425 TypeSpecification spec = type as TypeSpecification;
3426 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
3427 .Append (RefTypeModifier);
3430 protected virtual string PointerModifier {
3434 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3436 TypeSpecification spec = type as TypeSpecification;
3437 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
3438 .Append (PointerModifier);
3441 protected virtual char[] GenericTypeContainer {
3442 get {return new char[]{'<', '>'};}
3445 protected virtual char NestedTypeSeparator {
3449 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
3451 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3452 type is GenericInstanceType ? type.GetElementType () : type);
3453 List<TypeReference> genArgs = GetGenericArguments (type);
3456 bool insertNested = false;
3457 foreach (var decl in decls) {
3458 TypeReference declDef = decl.Resolve () ?? decl;
3460 buf.Append (NestedTypeSeparator);
3462 insertNested = true;
3463 AppendTypeName (buf, declDef, context);
3464 int ac = DocUtils.GetGenericArgumentCount (declDef);
3468 buf.Append (GenericTypeContainer [0]);
3469 var origState = MemberFormatterState;
3470 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3471 _AppendTypeName (buf, genArgs [argIdx++], context);
3472 for (int i = 1; i < c; ++i) {
3473 _AppendTypeName (buf.Append (","), genArgs [argIdx++], context);
3475 MemberFormatterState = origState;
3476 buf.Append (GenericTypeContainer [1]);
3482 protected List<TypeReference> GetGenericArguments (TypeReference type)
3484 var args = new List<TypeReference> ();
3485 GenericInstanceType inst = type as GenericInstanceType;
3487 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
3489 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
3493 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3498 protected virtual string GetConstructorName (MethodReference constructor)
3500 return constructor.Name;
3503 protected virtual string GetMethodName (MethodReference method)
3508 protected virtual string GetPropertyName (PropertyReference property)
3510 return property.Name;
3513 protected virtual string GetFieldName (FieldReference field)
3518 protected virtual string GetEventName (EventReference e)
3523 public virtual string GetDeclaration (MemberReference member)
3526 throw new ArgumentNullException ("member");
3527 TypeDefinition type = member as TypeDefinition;
3529 return GetTypeDeclaration (type);
3530 MethodDefinition method = member as MethodDefinition;
3531 if (method != null && method.IsConstructor)
3532 return GetConstructorDeclaration (method);
3534 return GetMethodDeclaration (method);
3535 PropertyDefinition prop = member as PropertyDefinition;
3537 return GetPropertyDeclaration (prop);
3538 FieldDefinition field = member as FieldDefinition;
3540 return GetFieldDeclaration (field);
3541 EventDefinition e = member as EventDefinition;
3543 return GetEventDeclaration (e);
3544 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3547 protected virtual string GetTypeDeclaration (TypeDefinition type)
3550 throw new ArgumentNullException ("type");
3551 StringBuilder buf = new StringBuilder (type.Name.Length);
3552 _AppendTypeName (buf, type, null);
3553 AppendGenericTypeConstraints (buf, type);
3554 return buf.ToString ();
3557 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
3559 return GetConstructorName (constructor);
3562 protected virtual string GetMethodDeclaration (MethodDefinition method)
3564 if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
3565 ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
3568 // Special signature for destructors.
3569 if (method.Name == "Finalize" && method.Parameters.Count == 0)
3570 return GetFinalizerName (method);
3572 StringBuilder buf = new StringBuilder ();
3574 AppendVisibility (buf, method);
3575 if (buf.Length == 0 &&
3576 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3579 AppendModifiers (buf, method);
3581 if (buf.Length != 0)
3583 buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
3585 AppendMethodName (buf, method);
3586 AppendGenericMethod (buf, method).Append (" ");
3587 AppendParameters (buf, method, method.Parameters);
3588 AppendGenericMethodConstraints (buf, method);
3589 return buf.ToString ();
3592 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3594 return buf.Append (method.Name);
3597 protected virtual string GetFinalizerName (MethodDefinition method)
3602 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3607 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3612 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3617 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
3622 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3627 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
3629 return GetPropertyName (property);
3632 protected virtual string GetFieldDeclaration (FieldDefinition field)
3634 return GetFieldName (field);
3637 protected virtual string GetEventDeclaration (EventDefinition e)
3639 return GetEventName (e);
3643 class ILFullMemberFormatter : MemberFormatter {
3645 public override string Language {
3646 get {return "ILAsm";}
3649 protected override char NestedTypeSeparator {
3655 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3657 if (GetBuiltinType (type.FullName) != null)
3659 string ns = DocUtils.GetNamespace (type);
3660 if (ns != null && ns.Length > 0) {
3661 if (type.IsValueType)
3662 buf.Append ("valuetype ");
3664 buf.Append ("class ");
3665 buf.Append (ns).Append ('.');
3670 private static string GetBuiltinType (string t)
3673 case "System.Byte": return "unsigned int8";
3674 case "System.SByte": return "int8";
3675 case "System.Int16": return "int16";
3676 case "System.Int32": return "int32";
3677 case "System.Int64": return "int64";
3678 case "System.IntPtr": return "native int";
3680 case "System.UInt16": return "unsigned int16";
3681 case "System.UInt32": return "unsigned int32";
3682 case "System.UInt64": return "unsigned int64";
3683 case "System.UIntPtr": return "native unsigned int";
3685 case "System.Single": return "float32";
3686 case "System.Double": return "float64";
3687 case "System.Boolean": return "bool";
3688 case "System.Char": return "char";
3689 case "System.Void": return "void";
3690 case "System.String": return "string";
3691 case "System.Object": return "object";
3696 protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
3698 return buf.Append (typename);
3701 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3703 if (type is GenericParameter) {
3704 AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
3708 string s = GetBuiltinType (type.FullName);
3710 return buf.Append (s);
3712 return base.AppendTypeName (buf, type, context);
3715 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
3717 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
3718 return buf.Append (type.Owner is TypeReference ? "!" : "!!");
3720 GenericParameterAttributes attrs = type.Attributes;
3721 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
3722 buf.Append ("class ");
3723 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
3724 buf.Append ("struct ");
3725 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
3726 buf.Append (".ctor ");
3727 IList<TypeReference> constraints = type.Constraints;
3728 MemberFormatterState = 0;
3729 if (constraints.Count > 0) {
3730 var full = new ILFullMemberFormatter ();
3731 buf.Append ("(").Append (full.GetName (constraints [0]));
3732 for (int i = 1; i < constraints.Count; ++i) {
3733 buf.Append (", ").Append (full.GetName (constraints [i]));
3737 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3739 if ((attrs & GenericParameterAttributes.Covariant) != 0)
3741 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
3746 protected override string GetTypeDeclaration (TypeDefinition type)
3748 string visibility = GetTypeVisibility (type.Attributes);
3749 if (visibility == null)
3752 StringBuilder buf = new StringBuilder ();
3754 buf.Append (".class ");
3756 buf.Append ("nested ");
3757 buf.Append (visibility).Append (" ");
3758 if (type.IsInterface)
3759 buf.Append ("interface ");
3760 if (type.IsSequentialLayout)
3761 buf.Append ("sequential ");
3762 if (type.IsAutoLayout)
3763 buf.Append ("auto ");
3764 if (type.IsAnsiClass)
3765 buf.Append ("ansi ");
3766 if (type.IsAbstract)
3767 buf.Append ("abstract ");
3768 if (type.IsSerializable)
3769 buf.Append ("serializable ");
3771 buf.Append ("sealed ");
3772 if (type.IsBeforeFieldInit)
3773 buf.Append ("beforefieldinit ");
3774 var state = MemberFormatterState;
3775 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3776 buf.Append (GetName (type));
3777 MemberFormatterState = state;
3778 var full = new ILFullMemberFormatter ();
3779 if (type.BaseType != null) {
3780 buf.Append (" extends ");
3781 if (type.BaseType.FullName == "System.Object")
3782 buf.Append ("System.Object");
3784 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
3787 foreach (var name in type.Interfaces
3788 .Select (i => full.GetName (i))
3789 .OrderBy (n => n)) {
3791 buf.Append (" implements ");
3800 return buf.ToString ();
3803 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
3805 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3806 type is GenericInstanceType ? type.GetElementType () : type);
3808 foreach (var decl in decls) {
3809 TypeReference declDef = decl.Resolve () ?? decl;
3811 buf.Append (NestedTypeSeparator);
3814 AppendTypeName (buf, declDef, context);
3818 foreach (TypeReference arg in GetGenericArguments (type)) {
3822 _AppendTypeName (buf, arg, context);
3828 static string GetTypeVisibility (TypeAttributes ta)
3830 switch (ta & TypeAttributes.VisibilityMask) {
3831 case TypeAttributes.Public:
3832 case TypeAttributes.NestedPublic:
3835 case TypeAttributes.NestedFamily:
3836 case TypeAttributes.NestedFamORAssem:
3844 protected override string GetConstructorDeclaration (MethodDefinition constructor)
3846 return GetMethodDeclaration (constructor);
3849 protected override string GetMethodDeclaration (MethodDefinition method)
3851 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
3854 var buf = new StringBuilder ();
3855 buf.Append (".method ");
3856 AppendVisibility (buf, method);
3857 if (method.IsStatic)
3858 buf.Append ("static ");
3859 if (method.IsHideBySig)
3860 buf.Append ("hidebysig ");
3861 if (method.IsPInvokeImpl) {
3862 var info = method.PInvokeInfo;
3863 buf.Append ("pinvokeimpl (\"")
3864 .Append (info.Module.Name)
3865 .Append ("\" as \"")
3866 .Append (info.EntryPoint)
3868 if (info.IsCharSetAuto)
3869 buf.Append (" auto");
3870 if (info.IsCharSetUnicode)
3871 buf.Append (" unicode");
3872 if (info.IsCharSetAnsi)
3873 buf.Append (" ansi");
3874 if (info.IsCallConvCdecl)
3875 buf.Append (" cdecl");
3876 if (info.IsCallConvStdCall)
3877 buf.Append (" stdcall");
3878 if (info.IsCallConvWinapi)
3879 buf.Append (" winapi");
3880 if (info.IsCallConvThiscall)
3881 buf.Append (" thiscall");
3882 if (info.SupportsLastError)
3883 buf.Append (" lasterr");
3886 if (method.IsSpecialName)
3887 buf.Append ("specialname ");
3888 if (method.IsRuntimeSpecialName)
3889 buf.Append ("rtspecialname ");
3890 if (method.IsNewSlot)
3891 buf.Append ("newslot ");
3892 if (method.IsVirtual)
3893 buf.Append ("virtual ");
3894 if (!method.IsStatic)
3895 buf.Append ("instance ");
3896 _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
3898 .Append (method.Name);
3899 if (method.IsGenericMethod ()) {
3900 var state = MemberFormatterState;
3901 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3902 IList<GenericParameter> args = method.GenericParameters;
3903 if (args.Count > 0) {
3905 _AppendTypeName (buf, args [0], null);
3906 for (int i = 1; i < args.Count; ++i)
3907 _AppendTypeName (buf.Append (", "), args [i], null);
3910 MemberFormatterState = state;
3915 for (int i = 0; i < method.Parameters.Count; ++i) {
3919 _AppendTypeName (buf, method.Parameters [i].ParameterType, new DynamicParserContext (method.Parameters [i]));
3921 buf.Append (method.Parameters [i].Name);
3925 buf.Append (" cil");
3926 if (method.IsRuntime)
3927 buf.Append (" runtime");
3928 if (method.IsManaged)
3929 buf.Append (" managed");
3931 return buf.ToString ();
3934 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3936 if (DocUtils.IsExplicitlyImplemented (method)) {
3937 TypeReference iface;
3938 MethodReference ifaceMethod;
3939 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3940 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3942 .Append (ifaceMethod.Name);
3944 return base.AppendMethodName (buf, method);
3947 protected override string RefTypeModifier {
3951 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3953 if (method.IsPublic)
3954 return buf.Append ("public ");
3955 if (method.IsFamilyAndAssembly)
3956 return buf.Append ("familyandassembly");
3957 if (method.IsFamilyOrAssembly)
3958 return buf.Append ("familyorassembly");
3959 if (method.IsFamily)
3960 return buf.Append ("family");
3964 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3966 string modifiers = String.Empty;
3967 if (method.IsStatic) modifiers += " static";
3968 if (method.IsVirtual && !method.IsAbstract) {
3969 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3970 else modifiers += " override";
3972 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
3973 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
3974 if (method.IsFinal) modifiers += " sealed";
3975 if (modifiers == " virtual sealed") modifiers = "";
3977 return buf.Append (modifiers);
3980 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3982 if (method.IsGenericMethod ()) {
3983 IList<GenericParameter> args = method.GenericParameters;
3984 if (args.Count > 0) {
3986 buf.Append (args [0].Name);
3987 for (int i = 1; i < args.Count; ++i)
3988 buf.Append (",").Append (args [i].Name);
3995 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
3997 return AppendParameters (buf, method, parameters, '(', ')');
4000 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4004 if (parameters.Count > 0) {
4005 if (DocUtils.IsExtensionMethod (method))
4006 buf.Append ("this ");
4007 AppendParameter (buf, parameters [0]);
4008 for (int i = 1; i < parameters.Count; ++i) {
4010 AppendParameter (buf, parameters [i]);
4014 return buf.Append (end);
4017 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4019 if (parameter.ParameterType is ByReferenceType) {
4020 if (parameter.IsOut)
4021 buf.Append ("out ");
4023 buf.Append ("ref ");
4025 buf.Append (GetName (parameter.ParameterType)).Append (" ");
4026 return buf.Append (parameter.Name);
4029 protected override string GetPropertyDeclaration (PropertyDefinition property)
4031 MethodDefinition gm = null, sm = null;
4033 string get_visible = null;
4034 if ((gm = property.GetMethod) != null &&
4035 (DocUtils.IsExplicitlyImplemented (gm) ||
4036 (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
4037 get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
4038 string set_visible = null;
4039 if ((sm = property.SetMethod) != null &&
4040 (DocUtils.IsExplicitlyImplemented (sm) ||
4041 (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
4042 set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
4044 if ((set_visible == null) && (get_visible == null))
4047 StringBuilder buf = new StringBuilder ()
4048 .Append (".property ");
4049 if (!(gm ?? sm).IsStatic)
4050 buf.Append ("instance ");
4051 _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
4052 buf.Append (' ').Append (property.Name);
4053 if (!property.HasParameters || property.Parameters.Count == 0)
4054 return buf.ToString ();
4058 foreach (ParameterDefinition p in property.Parameters) {
4062 _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
4066 return buf.ToString ();
4069 protected override string GetFieldDeclaration (FieldDefinition field)
4071 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4072 if (declType.IsEnum && field.Name == "value__")
4073 return null; // This member of enums aren't documented.
4075 StringBuilder buf = new StringBuilder ();
4076 AppendFieldVisibility (buf, field);
4077 if (buf.Length == 0)
4080 buf.Insert (0, ".field ");
4083 buf.Append ("static ");
4084 if (field.IsInitOnly)
4085 buf.Append ("initonly ");
4086 if (field.IsLiteral)
4087 buf.Append ("literal ");
4088 _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
4089 buf.Append (' ').Append (field.Name);
4090 AppendFieldValue (buf, field);
4092 return buf.ToString ();
4095 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4098 return buf.Append ("public ");
4099 if (field.IsFamilyAndAssembly)
4100 return buf.Append ("familyandassembly ");
4101 if (field.IsFamilyOrAssembly)
4102 return buf.Append ("familyorassembly ");
4104 return buf.Append ("family ");
4108 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4110 // enums have a value__ field, which we ignore
4111 if (field.DeclaringType.IsGenericType ())
4113 if (field.HasConstant && field.IsLiteral) {
4116 val = field.Constant;
4121 buf.Append (" = ").Append ("null");
4122 else if (val is Enum)
4124 .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4126 .Append (val.ToString ())
4128 else if (val is IFormattable) {
4129 string value = ((IFormattable)val).ToString();
4132 buf.Append ("\"" + value + "\"");
4134 buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4143 protected override string GetEventDeclaration (EventDefinition e)
4145 StringBuilder buf = new StringBuilder ();
4146 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4151 buf.Append (".event ")
4152 .Append (GetName (e.EventType))
4156 return buf.ToString ();
4160 class ILMemberFormatter : ILFullMemberFormatter {
4161 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4167 class CSharpFullMemberFormatter : MemberFormatter {
4169 public override string Language {
4173 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4175 string ns = DocUtils.GetNamespace (type);
4176 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
4177 buf.Append (ns).Append ('.');
4181 private string GetCSharpType (string t)
4184 case "System.Byte": return "byte";
4185 case "System.SByte": return "sbyte";
4186 case "System.Int16": return "short";
4187 case "System.Int32": return "int";
4188 case "System.Int64": return "long";
4190 case "System.UInt16": return "ushort";
4191 case "System.UInt32": return "uint";
4192 case "System.UInt64": return "ulong";
4194 case "System.Single": return "float";
4195 case "System.Double": return "double";
4196 case "System.Decimal": return "decimal";
4197 case "System.Boolean": return "bool";
4198 case "System.Char": return "char";
4199 case "System.Void": return "void";
4200 case "System.String": return "string";
4201 case "System.Object": return "object";
4206 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4208 if (context != null && context.TransformFlags != null &&
4209 (context.TransformFlags.Count == 0 || context.TransformFlags [context.TransformIndex])) {
4210 context.TransformIndex++;
4211 return buf.Append ("dynamic");
4214 if (type is GenericParameter)
4215 return AppendGenericParameterConstraints (buf, (GenericParameter) type, context).Append (type.Name);
4216 string t = type.FullName;
4217 if (!t.StartsWith ("System.")) {
4218 return base.AppendTypeName (buf, type, context);
4221 string s = GetCSharpType (t);
4223 if (context != null)
4224 context.TransformIndex++;
4225 return buf.Append (s);
4228 return base.AppendTypeName (buf, type, context);
4231 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
4233 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
4235 GenericParameterAttributes attrs = type.Attributes;
4236 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
4237 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
4241 buf.Append ("out ");
4245 protected override string GetTypeDeclaration (TypeDefinition type)
4247 string visibility = GetTypeVisibility (type.Attributes);
4248 if (visibility == null)
4251 StringBuilder buf = new StringBuilder ();
4253 buf.Append (visibility);
4256 MemberFormatter full = new CSharpFullMemberFormatter ();
4258 if (DocUtils.IsDelegate (type)) {
4259 buf.Append("delegate ");
4260 MethodDefinition invoke = type.GetMethod ("Invoke");
4261 buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
4262 buf.Append (GetName (type));
4263 AppendParameters (buf, invoke, invoke.Parameters);
4264 AppendGenericTypeConstraints (buf, type);
4267 return buf.ToString();
4270 if (type.IsAbstract && !type.IsInterface)
4271 buf.Append("abstract ");
4272 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
4273 buf.Append("sealed ");
4274 buf.Replace ("abstract sealed", "static");
4276 buf.Append (GetTypeKind (type));
4278 buf.Append (GetCSharpType (type.FullName) == null
4283 TypeReference basetype = type.BaseType;
4284 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
4287 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
4288 .Select (iface => full.GetName (iface))
4292 if (basetype != null || interface_names.Count > 0)
4295 if (basetype != null) {
4296 buf.Append (full.GetName (basetype));
4297 if (interface_names.Count > 0)
4301 for (int i = 0; i < interface_names.Count; i++){
4304 buf.Append (interface_names [i]);
4306 AppendGenericTypeConstraints (buf, type);
4309 return buf.ToString ();
4312 static string GetTypeKind (TypeDefinition t)
4318 if (t.IsClass || t.FullName == "System.Enum")
4322 throw new ArgumentException(t.FullName);
4325 static string GetTypeVisibility (TypeAttributes ta)
4327 switch (ta & TypeAttributes.VisibilityMask) {
4328 case TypeAttributes.Public:
4329 case TypeAttributes.NestedPublic:
4332 case TypeAttributes.NestedFamily:
4333 case TypeAttributes.NestedFamORAssem:
4341 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4343 if (type.GenericParameters.Count == 0)
4345 return AppendConstraints (buf, type.GenericParameters);
4348 private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
4350 foreach (GenericParameter genArg in genArgs) {
4351 GenericParameterAttributes attrs = genArg.Attributes;
4352 IList<TypeReference> constraints = genArg.Constraints;
4353 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
4356 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
4357 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
4358 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
4361 if (!isref && !isvt && !isnew && constraints.Count == 0)
4363 buf.Append (" where ").Append (genArg.Name).Append (" : ");
4365 buf.Append ("class");
4369 buf.Append ("struct");
4372 if (constraints.Count > 0 && !isvt) {
4375 buf.Append (GetTypeName (constraints [0]));
4376 for (int i = 1; i < constraints.Count; ++i)
4377 buf.Append (", ").Append (GetTypeName (constraints [i]));
4379 if (isnew && !isvt) {
4382 buf.Append ("new()");
4388 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4390 StringBuilder buf = new StringBuilder ();
4391 AppendVisibility (buf, constructor);
4392 if (buf.Length == 0)
4396 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
4397 AppendParameters (buf, constructor, constructor.Parameters);
4400 return buf.ToString ();
4403 protected override string GetMethodDeclaration (MethodDefinition method)
4405 string decl = base.GetMethodDeclaration (method);
4411 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4413 if (DocUtils.IsExplicitlyImplemented (method)) {
4414 TypeReference iface;
4415 MethodReference ifaceMethod;
4416 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4417 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4419 .Append (ifaceMethod.Name);
4421 return base.AppendMethodName (buf, method);
4424 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4426 if (method.GenericParameters.Count == 0)
4428 return AppendConstraints (buf, method.GenericParameters);
4431 protected override string RefTypeModifier {
4435 protected override string GetFinalizerName (MethodDefinition method)
4437 return "~" + method.DeclaringType.Name + " ()";
4440 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4444 if (method.IsPublic)
4445 return buf.Append ("public");
4446 if (method.IsFamily || method.IsFamilyOrAssembly)
4447 return buf.Append ("protected");
4451 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4453 string modifiers = String.Empty;
4454 if (method.IsStatic) modifiers += " static";
4455 if (method.IsVirtual && !method.IsAbstract) {
4456 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4457 else modifiers += " override";
4459 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4460 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4461 if (method.IsFinal) modifiers += " sealed";
4462 if (modifiers == " virtual sealed") modifiers = "";
4464 return buf.Append (modifiers);
4467 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4469 if (method.IsGenericMethod ()) {
4470 IList<GenericParameter> args = method.GenericParameters;
4471 if (args.Count > 0) {
4473 buf.Append (args [0].Name);
4474 for (int i = 1; i < args.Count; ++i)
4475 buf.Append (",").Append (args [i].Name);
4482 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4484 return AppendParameters (buf, method, parameters, '(', ')');
4487 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4491 if (parameters.Count > 0) {
4492 if (DocUtils.IsExtensionMethod (method))
4493 buf.Append ("this ");
4494 AppendParameter (buf, parameters [0]);
4495 for (int i = 1; i < parameters.Count; ++i) {
4497 AppendParameter (buf, parameters [i]);
4501 return buf.Append (end);
4504 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4506 if (parameter.ParameterType is ByReferenceType) {
4507 if (parameter.IsOut)
4508 buf.Append ("out ");
4510 buf.Append ("ref ");
4512 buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
4513 buf.Append (parameter.Name);
4514 if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant) {
4515 buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
4520 protected override string GetPropertyDeclaration (PropertyDefinition property)
4522 MethodDefinition method;
4524 string get_visible = null;
4525 if ((method = property.GetMethod) != null &&
4526 (DocUtils.IsExplicitlyImplemented (method) ||
4527 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
4528 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
4529 string set_visible = null;
4530 if ((method = property.SetMethod) != null &&
4531 (DocUtils.IsExplicitlyImplemented (method) ||
4532 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
4533 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
4535 if ((set_visible == null) && (get_visible == null))
4539 StringBuilder buf = new StringBuilder ();
4540 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
4541 buf.Append (visibility = get_visible);
4542 else if (set_visible != null && get_visible == null)
4543 buf.Append (visibility = set_visible);
4545 buf.Append (visibility = "public");
4547 // Pick an accessor to use for static/virtual/override/etc. checks.
4548 method = property.SetMethod;
4550 method = property.GetMethod;
4552 string modifiers = String.Empty;
4553 if (method.IsStatic) modifiers += " static";
4554 if (method.IsVirtual && !method.IsAbstract) {
4555 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
4556 modifiers += " virtual";
4558 modifiers += " override";
4560 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
4561 if (method.IsAbstract && !declDef.IsInterface)
4562 modifiers += " abstract";
4564 modifiers += " sealed";
4565 if (modifiers == " virtual sealed")
4567 buf.Append (modifiers).Append (' ');
4569 buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
4571 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
4572 string name = property.Name;
4573 foreach (MemberReference mi in defs) {
4574 if (mi == property) {
4579 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
4581 if (property.Parameters.Count != 0) {
4582 AppendParameters (buf, method, property.Parameters, '[', ']');
4586 if (get_visible != null) {
4587 if (get_visible != visibility)
4588 buf.Append (' ').Append (get_visible);
4589 buf.Append (" get;");
4591 if (set_visible != null) {
4592 if (set_visible != visibility)
4593 buf.Append (' ').Append (set_visible);
4594 buf.Append (" set;");
4598 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
4601 protected override string GetFieldDeclaration (FieldDefinition field)
4603 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4604 if (declType.IsEnum && field.Name == "value__")
4605 return null; // This member of enums aren't documented.
4607 StringBuilder buf = new StringBuilder ();
4608 AppendFieldVisibility (buf, field);
4609 if (buf.Length == 0)
4612 if (declType.IsEnum)
4615 if (field.IsStatic && !field.IsLiteral)
4616 buf.Append (" static");
4617 if (field.IsInitOnly)
4618 buf.Append (" readonly");
4619 if (field.IsLiteral)
4620 buf.Append (" const");
4622 buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
4623 buf.Append (field.Name);
4624 AppendFieldValue (buf, field);
4627 return buf.ToString ();
4630 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4633 return buf.Append ("public");
4634 if (field.IsFamily || field.IsFamilyOrAssembly)
4635 return buf.Append ("protected");
4639 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4641 // enums have a value__ field, which we ignore
4642 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
4643 field.DeclaringType.IsGenericType ())
4645 if (field.HasConstant && field.IsLiteral) {
4648 val = field.Constant;
4653 buf.Append (" = ").Append ("null");
4654 else if (val is Enum)
4655 buf.Append (" = ").Append (val.ToString ());
4656 else if (val is IFormattable) {
4657 string value = ((IFormattable)val).ToString();
4659 value = "\"" + value + "\"";
4660 buf.Append (" = ").Append (value);
4666 protected override string GetEventDeclaration (EventDefinition e)
4668 StringBuilder buf = new StringBuilder ();
4669 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4673 AppendModifiers (buf, e.AddMethod);
4675 buf.Append (" event ");
4676 buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters [0]))).Append (' ');
4677 buf.Append (e.Name).Append (';');
4679 return buf.ToString ();
4683 class CSharpMemberFormatter : CSharpFullMemberFormatter {
4684 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4690 class DocTypeFullMemberFormatter : MemberFormatter {
4691 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
4693 protected override char NestedTypeSeparator {
4698 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
4699 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4705 class SlashDocMemberFormatter : MemberFormatter {
4707 protected override char[] GenericTypeContainer {
4708 get {return new char[]{'{', '}'};}
4711 private bool AddTypeCount = true;
4713 private TypeReference genDeclType;
4714 private MethodReference genDeclMethod;
4716 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4718 if (type is GenericParameter) {
4720 if (genDeclType != null) {
4721 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
4722 for (int i = 0; i < genArgs.Count; ++i) {
4723 if (genArgs [i].Name == type.Name) {
4724 buf.Append ('`').Append (i);
4729 if (genDeclMethod != null) {
4730 IList<GenericParameter> genArgs = null;
4731 if (genDeclMethod.IsGenericMethod ()) {
4732 genArgs = genDeclMethod.GenericParameters;
4733 for (int i = 0; i < genArgs.Count; ++i) {
4734 if (genArgs [i].Name == type.Name) {
4735 buf.Append ("``").Append (i);
4741 if (genDeclType == null && genDeclMethod == null) {
4742 // Probably from within an explicitly implemented interface member,
4743 // where CSC uses parameter names instead of indices (why?), e.g.
4744 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
4745 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
4746 buf.Append (type.Name);
4748 if (buf.Length == l) {
4749 throw new Exception (string.Format (
4750 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
4751 type.Name, genDeclType, genDeclMethod));
4755 base.AppendTypeName (buf, type, context);
4757 int numArgs = type.GenericParameters.Count;
4758 if (type.DeclaringType != null)
4759 numArgs -= type.GenericParameters.Count;
4761 buf.Append ('`').Append (numArgs);
4768 protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
4770 buf.Append (ArrayDelimeters [0]);
4771 int rank = array.Rank;
4774 for (int i = 1; i < rank; ++i) {
4778 return buf.Append (ArrayDelimeters [1]);
4781 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4784 base.AppendGenericType (buf, type, context);
4786 AppendType (buf, type, context);
4790 private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4792 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
4793 bool insertNested = false;
4794 int prevParamCount = 0;
4795 foreach (var decl in decls) {
4797 buf.Append (NestedTypeSeparator);
4798 insertNested = true;
4799 base.AppendTypeName (buf, decl, context);
4800 int argCount = DocUtils.GetGenericArgumentCount (decl);
4801 int numArgs = argCount - prevParamCount;
4802 prevParamCount = argCount;
4804 buf.Append ('`').Append (numArgs);
4809 public override string GetDeclaration (MemberReference member)
4811 TypeReference r = member as TypeReference;
4813 return "T:" + GetTypeName (r);
4815 return base.GetDeclaration (member);
4818 protected override string GetConstructorName (MethodReference constructor)
4820 return GetMethodDefinitionName (constructor, "#ctor");
4823 protected override string GetMethodName (MethodReference method)
4826 MethodDefinition methodDef = method as MethodDefinition;
4827 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
4830 TypeReference iface;
4831 MethodReference ifaceMethod;
4832 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
4833 AddTypeCount = false;
4834 name = GetTypeName (iface) + "." + ifaceMethod.Name;
4835 AddTypeCount = true;
4837 return GetMethodDefinitionName (method, name);
4840 private string GetMethodDefinitionName (MethodReference method, string name)
4842 StringBuilder buf = new StringBuilder ();
4843 buf.Append (GetTypeName (method.DeclaringType));
4845 buf.Append (name.Replace (".", "#"));
4846 if (method.IsGenericMethod ()) {
4847 IList<GenericParameter> genArgs = method.GenericParameters;
4848 if (genArgs.Count > 0)
4849 buf.Append ("``").Append (genArgs.Count);
4851 IList<ParameterDefinition> parameters = method.Parameters;
4853 genDeclType = method.DeclaringType;
4854 genDeclMethod = method;
4855 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
4859 genDeclMethod = null;
4861 return buf.ToString ();
4864 private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
4866 if (parameters.Count == 0)
4871 AppendParameter (buf, genArgs, parameters [0]);
4872 for (int i = 1; i < parameters.Count; ++i) {
4874 AppendParameter (buf, genArgs, parameters [i]);
4877 return buf.Append (')');
4880 private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
4882 AddTypeCount = false;
4883 buf.Append (GetTypeName (parameter.ParameterType));
4884 AddTypeCount = true;
4888 protected override string GetPropertyName (PropertyReference property)
4892 PropertyDefinition propertyDef = property as PropertyDefinition;
4893 MethodDefinition method = null;
4894 if (propertyDef != null)
4895 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
4896 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
4897 name = property.Name;
4899 TypeReference iface;
4900 MethodReference ifaceMethod;
4901 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4902 AddTypeCount = false;
4903 name = string.Join ("#", new string[]{
4904 GetTypeName (iface).Replace (".", "#"),
4905 DocUtils.GetMember (property.Name)
4907 AddTypeCount = true;
4910 StringBuilder buf = new StringBuilder ();
4911 buf.Append (GetName (property.DeclaringType));
4914 IList<ParameterDefinition> parameters = property.Parameters;
4915 if (parameters.Count > 0) {
4916 genDeclType = property.DeclaringType;
4918 IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
4919 AppendParameter (buf, genArgs, parameters [0]);
4920 for (int i = 1; i < parameters.Count; ++i) {
4922 AppendParameter (buf, genArgs, parameters [i]);
4927 return buf.ToString ();
4930 protected override string GetFieldName (FieldReference field)
4932 return string.Format ("{0}.{1}",
4933 GetName (field.DeclaringType), field.Name);
4936 protected override string GetEventName (EventReference e)
4938 return string.Format ("{0}.{1}",
4939 GetName (e.DeclaringType), e.Name);
4942 protected override string GetTypeDeclaration (TypeDefinition type)
4944 string name = GetName (type);
4950 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4952 string name = GetName (constructor);
4958 protected override string GetMethodDeclaration (MethodDefinition method)
4960 string name = GetName (method);
4963 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4964 genDeclType = method.DeclaringType;
4965 genDeclMethod = method;
4966 name += "~" + GetName (method.ReturnType);
4968 genDeclMethod = null;
4973 protected override string GetPropertyDeclaration (PropertyDefinition property)
4975 string name = GetName (property);
4981 protected override string GetFieldDeclaration (FieldDefinition field)
4983 string name = GetName (field);
4989 protected override string GetEventDeclaration (EventDefinition e)
4991 string name = GetName (e);
4998 class FileNameMemberFormatter : SlashDocMemberFormatter {
4999 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5004 protected override char NestedTypeSeparator {