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 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
1817 string cref = slashdocFormatter.GetDeclaration (source.Exception);
1818 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
1821 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
1822 e.SetAttribute ("cref", cref);
1823 e.InnerXml = "To be added; from: <see cref=\"" +
1824 string.Join ("\" />, <see cref=\"",
1825 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
1828 docs.AppendChild (e);
1830 SortXmlNodes (docs, docs.SelectNodes ("exception"),
1831 new CrefComparer ());
1834 private static void NormalizeWhitespace(XmlElement e) {
1835 // Remove all text and whitespace nodes from the element so it
1836 // is outputted with nice indentation and no blank lines.
1837 ArrayList deleteNodes = new ArrayList();
1838 foreach (XmlNode n in e)
1839 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
1841 foreach (XmlNode n in deleteNodes)
1842 n.ParentNode.RemoveChild(n);
1845 private static bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
1847 TypeDefinition type = member as TypeDefinition;
1849 type = member.DeclaringType as TypeDefinition;
1850 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
1853 private static string GetAssemblyVersion (AssemblyDefinition assembly)
1855 return assembly.Name.Version.ToString();
1858 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
1860 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
1862 e = root.OwnerDocument.CreateElement("AssemblyInfo");
1863 root.AppendChild(e);
1865 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
1866 .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
1868 // matches.Count > 0 && add: ignore -- already present
1869 if (matches.Count > 0 && !add) {
1870 foreach (XmlNode c in matches)
1873 else if (matches.Count == 0 && add) {
1874 foreach (string sv in assemblyVersions) {
1875 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
1880 // matches.Count == 0 && !add: ignore -- already not present
1882 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
1883 SortXmlNodes (e, avs, new VersionComparer ());
1885 return avs.Count != 0;
1888 // FIXME: get TypeReferences instead of string comparison?
1889 private static string[] IgnorableAttributes = {
1890 // Security related attributes
1891 "System.Reflection.AssemblyKeyFileAttribute",
1892 "System.Reflection.AssemblyDelaySignAttribute",
1893 // Present in @RefType
1894 "System.Runtime.InteropServices.OutAttribute",
1895 // For naming the indexer to use when not using indexers
1896 "System.Reflection.DefaultMemberAttribute",
1897 // for decimal constants
1898 "System.Runtime.CompilerServices.DecimalConstantAttribute",
1899 // compiler generated code
1900 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
1901 // more compiler generated code, e.g. iterator methods
1902 "System.Diagnostics.DebuggerHiddenAttribute",
1903 "System.Runtime.CompilerServices.FixedBufferAttribute",
1904 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
1905 // extension methods
1906 "System.Runtime.CompilerServices.ExtensionAttribute",
1907 // Used to differentiate 'object' from C#4 'dynamic'
1908 "System.Runtime.CompilerServices.DynamicAttribute",
1911 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes)
1913 if (!attributes.Any ()) {
1914 ClearElement (root, "Attributes");
1918 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
1922 e = root.OwnerDocument.CreateElement("Attributes");
1924 foreach (string attribute in attributes) {
1925 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
1928 WriteElementText(ae, "AttributeName", attribute);
1931 if (e.ParentNode == null)
1932 root.AppendChild(e);
1934 NormalizeWhitespace(e);
1937 public static string MakeAttributesValueString (object v, TypeReference valueType)
1941 if (valueType.FullName == "System.Type")
1942 return "typeof(" + v.ToString () + ")";
1943 if (valueType.FullName == "System.String")
1944 return "\"" + v.ToString () + "\"";
1945 if (valueType.FullName == "System.Char")
1946 return "'" + v.ToString () + "'";
1948 return (bool)v ? "true" : "false";
1949 TypeDefinition valueDef = valueType.Resolve ();
1950 if (valueDef == null || !valueDef.IsEnum)
1951 return v.ToString ();
1952 string typename = GetDocTypeFullName (valueType);
1953 var values = GetEnumerationValues (valueDef);
1954 long c = ToInt64 (v);
1955 if (values.ContainsKey (c))
1956 return typename + "." + values [c];
1957 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
1958 return string.Join (" | ",
1959 (from i in values.Keys
1961 select typename + "." + values [i])
1962 .DefaultIfEmpty (v.ToString ()).ToArray ());
1964 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
1967 private static Dictionary<long, string> GetEnumerationValues (TypeDefinition type)
1969 var values = new Dictionary<long, string> ();
1971 (from f in type.Fields
1972 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
1974 values [ToInt64 (f.Constant)] = f.Name;
1979 static long ToInt64 (object value)
1982 return (long) (ulong) value;
1983 return Convert.ToInt64 (value);
1986 private void MakeParameters (XmlElement root, IList<ParameterDefinition> parameters)
1988 XmlElement e = WriteElement(root, "Parameters");
1990 foreach (ParameterDefinition p in parameters) {
1991 XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
1993 pe.SetAttribute("Name", p.Name);
1994 pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
1995 if (p.ParameterType is ByReferenceType) {
1996 if (p.IsOut) pe.SetAttribute("RefType", "out");
1997 else pe.SetAttribute("RefType", "ref");
1999 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
2003 private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams)
2005 if (typeParams == null || typeParams.Count == 0) {
2006 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2008 root.RemoveChild (f);
2011 XmlElement e = WriteElement(root, "TypeParameters");
2013 foreach (GenericParameter t in typeParams) {
2014 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2016 pe.SetAttribute("Name", t.Name);
2017 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""));
2018 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2019 IList<TypeReference> constraints = t.Constraints;
2020 GenericParameterAttributes attrs = t.Attributes;
2021 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2029 ce = root.OwnerDocument.CreateElement ("Constraints");
2031 pe.AppendChild (ce);
2032 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2033 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2034 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2035 AppendElementText (ce, "ParameterAttribute", "Covariant");
2036 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2037 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2038 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2039 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2040 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2041 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2042 foreach (TypeReference c in constraints) {
2043 TypeDefinition cd = c.Resolve ();
2044 AppendElementText (ce,
2045 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2046 GetDocTypeFullName (c));
2051 private void MakeParameters (XmlElement root, MemberReference mi)
2053 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2054 MakeParameters (root, ((MethodDefinition)mi).Parameters);
2055 else if (mi is MethodDefinition) {
2056 MethodDefinition mb = (MethodDefinition) mi;
2057 IList<ParameterDefinition> parameters = mb.Parameters;
2058 MakeParameters(root, parameters);
2059 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2060 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2061 p.SetAttribute ("RefType", "this");
2064 else if (mi is PropertyDefinition) {
2065 IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
2066 if (parameters.Count > 0)
2067 MakeParameters(root, parameters);
2071 else if (mi is FieldDefinition) return;
2072 else if (mi is EventDefinition) return;
2073 else throw new ArgumentException();
2076 internal static string GetDocParameterType (TypeReference type)
2078 return GetDocTypeFullName (type).Replace ("@", "&");
2081 private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes)
2083 XmlElement e = WriteElement(root, "ReturnValue");
2085 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2086 if (attributes != null)
2087 MakeAttributes(e, GetCustomAttributes (attributes, ""));
2090 private void MakeReturnValue (XmlElement root, MemberReference mi)
2092 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2094 else if (mi is MethodDefinition)
2095 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes);
2096 else if (mi is PropertyDefinition)
2097 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
2098 else if (mi is FieldDefinition)
2099 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
2100 else if (mi is EventDefinition)
2101 MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
2103 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2106 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2108 MemberReference mi = info.Member;
2109 if (mi is TypeDefinition) return null;
2111 string sigs = memberFormatters [0].GetDeclaration (mi);
2112 if (sigs == null) return null; // not publicly visible
2114 // no documentation for property/event accessors. Is there a better way of doing this?
2115 if (mi.Name.StartsWith("get_")) return null;
2116 if (mi.Name.StartsWith("set_")) return null;
2117 if (mi.Name.StartsWith("add_")) return null;
2118 if (mi.Name.StartsWith("remove_")) return null;
2119 if (mi.Name.StartsWith("raise_")) return null;
2121 XmlElement me = doc.CreateElement("Member");
2122 me.SetAttribute("MemberName", GetMemberName (mi));
2126 if (exceptions.HasValue &&
2127 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2128 UpdateExceptions (info.Node, info.Member);
2130 if (since != null) {
2131 XmlNode docs = me.SelectSingleNode("Docs");
2132 docs.AppendChild (CreateSinceNode (doc));
2138 internal static string GetMemberName (MemberReference mi)
2140 MethodDefinition mb = mi as MethodDefinition;
2142 PropertyDefinition pi = mi as PropertyDefinition;
2145 return DocUtils.GetPropertyName (pi);
2147 StringBuilder sb = new StringBuilder (mi.Name.Length);
2148 if (!DocUtils.IsExplicitlyImplemented (mb))
2149 sb.Append (mi.Name);
2151 TypeReference iface;
2152 MethodReference ifaceMethod;
2153 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2154 sb.Append (GetDocTypeFullName (iface));
2156 sb.Append (ifaceMethod.Name);
2158 if (mb.IsGenericMethod ()) {
2159 IList<GenericParameter> typeParams = mb.GenericParameters;
2160 if (typeParams.Count > 0) {
2162 sb.Append (typeParams [0].Name);
2163 for (int i = 1; i < typeParams.Count; ++i)
2164 sb.Append (",").Append (typeParams [i].Name);
2168 return sb.ToString ();
2171 /// SIGNATURE GENERATION FUNCTIONS
2172 internal static bool IsPrivate (MemberReference mi)
2174 return memberFormatters [0].GetDeclaration (mi) == null;
2177 internal static string GetMemberType (MemberReference mi)
2179 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2180 return "Constructor";
2181 if (mi is MethodDefinition)
2183 if (mi is PropertyDefinition)
2185 if (mi is FieldDefinition)
2187 if (mi is EventDefinition)
2189 throw new ArgumentException();
2192 private static string GetDocTypeName (TypeReference type)
2194 return docTypeFormatter.GetName (type);
2197 internal static string GetDocTypeFullName (TypeReference type)
2199 return DocTypeFullMemberFormatter.Default.GetName (type);
2202 internal static string GetXPathForMember (DocumentationMember member)
2204 StringBuilder xpath = new StringBuilder ();
2205 xpath.Append ("//Members/Member[@MemberName=\"")
2206 .Append (member.MemberName)
2208 if (member.Parameters != null && member.Parameters.Count > 0) {
2209 xpath.Append ("/Parameters[count(Parameter) = ")
2210 .Append (member.Parameters.Count);
2211 for (int i = 0; i < member.Parameters.Count; ++i) {
2212 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2213 xpath.Append (member.Parameters [i]);
2214 xpath.Append ("\"");
2216 xpath.Append ("]/..");
2218 return xpath.ToString ();
2221 public static string GetXPathForMember (XPathNavigator member)
2223 StringBuilder xpath = new StringBuilder ();
2224 xpath.Append ("//Type[@FullName=\"")
2225 .Append (member.SelectSingleNode ("../../@FullName").Value)
2227 xpath.Append ("Members/Member[@MemberName=\"")
2228 .Append (member.SelectSingleNode ("@MemberName").Value)
2230 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2231 if (parameters.Count > 0) {
2232 xpath.Append ("/Parameters[count(Parameter) = ")
2233 .Append (parameters.Count);
2235 while (parameters.MoveNext ()) {
2237 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2238 xpath.Append (parameters.Current.Value);
2239 xpath.Append ("\"");
2241 xpath.Append ("]/..");
2243 return xpath.ToString ();
2246 public static string GetXPathForMember (MemberReference member)
2248 StringBuilder xpath = new StringBuilder ();
2249 xpath.Append ("//Type[@FullName=\"")
2250 .Append (member.DeclaringType.FullName)
2252 xpath.Append ("Members/Member[@MemberName=\"")
2253 .Append (GetMemberName (member))
2256 IList<ParameterDefinition> parameters = null;
2257 if (member is MethodDefinition)
2258 parameters = ((MethodDefinition) member).Parameters;
2259 else if (member is PropertyDefinition) {
2260 parameters = ((PropertyDefinition) member).Parameters;
2262 if (parameters != null && parameters.Count > 0) {
2263 xpath.Append ("/Parameters[count(Parameter) = ")
2264 .Append (parameters.Count);
2265 for (int i = 0; i < parameters.Count; ++i) {
2266 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2267 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2268 xpath.Append ("\"");
2270 xpath.Append ("]/..");
2272 return xpath.ToString ();
2276 static class CecilExtensions {
2277 public static string GetDeclaringType(this CustomAttribute attribute)
2279 return attribute.Constructor.DeclaringType.FullName;
2282 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
2284 foreach (var c in type.Methods.Where (m => m.IsConstructor))
2285 yield return (MemberReference) c;
2286 foreach (var e in type.Events)
2287 yield return (MemberReference) e;
2288 foreach (var f in type.Fields)
2289 yield return (MemberReference) f;
2290 foreach (var m in type.Methods.Where (m => !m.IsConstructor))
2291 yield return (MemberReference) m;
2292 foreach (var t in type.NestedTypes)
2293 yield return (MemberReference) t;
2294 foreach (var p in type.Properties)
2295 yield return (MemberReference) p;
2298 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
2300 return GetMembers (type).Where (m => m.Name == member);
2303 public static MemberReference GetMember (this TypeDefinition type, string member)
2305 return GetMembers (type, member).EnsureZeroOrOne ();
2308 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2310 if (source.Count () > 1)
2311 throw new InvalidOperationException ("too many matches");
2312 return source.FirstOrDefault ();
2315 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2318 .Where (m => m.Name == method)
2319 .EnsureZeroOrOne ();
2322 public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
2324 TypeDefinition def = type as TypeDefinition;
2326 return new MemberReference [0];
2327 CustomAttribute defMemberAttr = def.CustomAttributes
2328 .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
2329 if (defMemberAttr == null)
2330 return new MemberReference [0];
2331 string name = (string) defMemberAttr.ConstructorArguments [0].Value;
2332 return def.Properties
2333 .Where (p => p.Name == name)
2334 .Select (p => (MemberReference) p);
2337 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2339 return assembly.Modules.SelectMany (md => md.GetAllTypes ());
2342 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2344 return GetTypes (assembly)
2345 .Where (td => td.FullName == type)
2346 .EnsureZeroOrOne ();
2349 public static bool IsGenericType (this TypeReference type)
2351 return type.GenericParameters.Count > 0;
2354 public static bool IsGenericMethod (this MethodReference method)
2356 return method.GenericParameters.Count > 0;
2359 public static MemberReference Resolve (this MemberReference member)
2361 FieldReference fr = member as FieldReference;
2363 return fr.Resolve ();
2364 MethodReference mr = member as MethodReference;
2366 return mr.Resolve ();
2367 TypeReference tr = member as TypeReference;
2369 return tr.Resolve ();
2370 PropertyReference pr = member as PropertyReference;
2373 EventReference er = member as EventReference;
2376 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2379 public static TypeReference GetUnderlyingType (this TypeDefinition type)
2383 return type.Fields.First (f => f.Name == "value__").FieldType;
2386 public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
2388 return self.Types.SelectMany (t => t.GetAllTypes ());
2391 static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
2395 if (!self.HasNestedTypes)
2398 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
2403 static class DocUtils {
2404 public static bool IsExplicitlyImplemented (MethodDefinition method)
2406 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2409 public static string GetTypeDotMember (string name)
2411 int startType, startMethod;
2412 startType = startMethod = -1;
2413 for (int i = 0; i < name.Length; ++i) {
2414 if (name [i] == '.') {
2415 startType = startMethod;
2419 return name.Substring (startType+1);
2422 public static string GetMember (string name)
2424 int i = name.LastIndexOf ('.');
2427 return name.Substring (i+1);
2430 public static void GetInfoForExplicitlyImplementedMethod (
2431 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2435 if (method.Overrides.Count != 1)
2436 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2437 iface = method.Overrides [0].DeclaringType;
2438 ifaceMethod = method.Overrides [0];
2441 public static string GetPropertyName (PropertyDefinition pi)
2443 // Issue: (g)mcs-generated assemblies that explicitly implement
2444 // properties don't specify the full namespace, just the
2445 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2446 MethodDefinition method = pi.GetMethod;
2448 method = pi.SetMethod;
2449 if (!IsExplicitlyImplemented (method))
2452 // Need to determine appropriate namespace for this member.
2453 TypeReference iface;
2454 MethodReference ifaceMethod;
2455 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2456 return string.Join (".", new string[]{
2457 DocTypeFullMemberFormatter.Default.GetName (iface),
2458 GetMember (pi.Name)});
2461 public static string GetNamespace (TypeReference type)
2463 if (type.GetElementType ().IsNested)
2464 type = type.GetElementType ();
2465 while (type != null && type.IsNested)
2466 type = type.DeclaringType;
2468 return string.Empty;
2469 return type.Namespace;
2472 public static string PathCombine (string dir, string path)
2478 return Path.Combine (dir, path);
2481 public static bool IsExtensionMethod (MethodDefinition method)
2484 method.CustomAttributes
2485 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2486 && method.DeclaringType.CustomAttributes
2487 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
2490 public static bool IsDelegate (TypeDefinition type)
2492 TypeReference baseRef = type.BaseType;
2493 if (baseRef == null)
2495 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2496 baseRef.FullName == "System.MulticastDelegate";
2499 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2501 List<TypeReference> decls = new List<TypeReference> ();
2503 while (type.DeclaringType != null) {
2504 decls.Add (type.DeclaringType);
2505 type = type.DeclaringType;
2511 public static int GetGenericArgumentCount (TypeReference type)
2513 GenericInstanceType inst = type as GenericInstanceType;
2515 ? inst.GenericArguments.Count
2516 : type.GenericParameters.Count;
2519 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
2521 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
2522 List<TypeReference> userInterfaces = new List<TypeReference> ();
2523 foreach (TypeReference iface in type.Interfaces) {
2524 TypeReference lookup = iface.Resolve () ?? iface;
2525 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
2526 userInterfaces.Add (iface);
2528 return userInterfaces;
2531 private static string GetQualifiedTypeName (TypeReference type)
2533 return "[" + type.Scope.Name + "]" + type.FullName;
2536 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
2538 HashSet<string> inheritedInterfaces = new HashSet<string> ();
2539 Action<TypeDefinition> a = null;
2541 if (t == null) return;
2542 foreach (TypeReference r in t.Interfaces) {
2543 inheritedInterfaces.Add (GetQualifiedTypeName (r));
2547 TypeReference baseRef = type.BaseType;
2548 while (baseRef != null) {
2549 TypeDefinition baseDef = baseRef.Resolve ();
2550 if (baseDef != null) {
2552 baseRef = baseDef.BaseType;
2557 foreach (TypeReference r in type.Interfaces)
2559 return inheritedInterfaces;
2563 class DocsNodeInfo {
2564 public DocsNodeInfo (XmlElement node)
2569 public DocsNodeInfo (XmlElement node, TypeDefinition type)
2575 public DocsNodeInfo (XmlElement node, MemberReference member)
2578 SetMemberInfo (member);
2581 void SetType (TypeDefinition type)
2584 throw new ArgumentNullException ("type");
2586 GenericParameters = new List<GenericParameter> (type.GenericParameters);
2587 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
2588 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
2589 for (int i = 0; i < declTypes.Count - 1; ++i) {
2590 int remove = System.Math.Min (maxGenArgs,
2591 DocUtils.GetGenericArgumentCount (declTypes [i]));
2592 maxGenArgs -= remove;
2593 while (remove-- > 0)
2594 GenericParameters.RemoveAt (0);
2596 if (DocUtils.IsDelegate (type)) {
2597 Parameters = type.GetMethod("Invoke").Parameters;
2598 ReturnType = type.GetMethod("Invoke").ReturnType;
2599 ReturnIsReturn = true;
2603 void SetMemberInfo (MemberReference member)
2606 throw new ArgumentNullException ("member");
2607 ReturnIsReturn = true;
2611 if (member is MethodReference ) {
2612 MethodReference mr = (MethodReference) member;
2613 Parameters = mr.Parameters;
2614 if (mr.IsGenericMethod ()) {
2615 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
2618 else if (member is PropertyDefinition) {
2619 Parameters = ((PropertyDefinition) member).Parameters;
2622 if (member is MethodDefinition) {
2623 ReturnType = ((MethodDefinition) member).ReturnType;
2624 } else if (member is PropertyDefinition) {
2625 ReturnType = ((PropertyDefinition) member).PropertyType;
2626 ReturnIsReturn = false;
2629 // no remarks section for enum members
2630 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
2634 public TypeReference ReturnType;
2635 public List<GenericParameter> GenericParameters;
2636 public IList<ParameterDefinition> Parameters;
2637 public bool ReturnIsReturn;
2638 public XmlElement Node;
2639 public bool AddRemarks = true;
2640 public MemberReference Member;
2641 public TypeDefinition Type;
2644 class DocumentationEnumerator {
2646 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2648 return GetDocumentationTypes (assembly, forTypes, null);
2651 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2653 foreach (TypeDefinition type in assembly.GetTypes()) {
2654 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
2656 if (seen != null && seen.Contains (type.FullName))
2659 foreach (TypeDefinition nested in type.NestedTypes)
2660 yield return nested;
2664 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2666 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
2667 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
2668 oldmember.RemoveAttribute ("__monodocer-seen__");
2671 MemberReference m = GetMember (type, new DocumentationMember (oldmember));
2673 yield return new DocsNodeInfo (oldmember);
2676 yield return new DocsNodeInfo (oldmember, m);
2681 protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
2683 string membertype = member.MemberType;
2685 string returntype = member.ReturnType;
2687 string docName = member.MemberName;
2688 string[] docTypeParams = GetTypeParameters (docName);
2690 // Loop through all members in this type with the same name
2691 foreach (MemberReference mi in GetReflectionMembers (type, docName)) {
2692 if (mi is TypeDefinition) continue;
2693 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
2695 if (MDocUpdater.IsPrivate (mi))
2698 IList<ParameterDefinition> pis = null;
2699 string[] typeParams = null;
2700 if (mi is MethodDefinition) {
2701 MethodDefinition mb = (MethodDefinition) mi;
2702 pis = mb.Parameters;
2703 if (docTypeParams != null && mb.IsGenericMethod ()) {
2704 IList<GenericParameter> args = mb.GenericParameters;
2705 if (args.Count == docTypeParams.Length) {
2706 typeParams = args.Select (p => p.Name).ToArray ();
2710 else if (mi is PropertyDefinition)
2711 pis = ((PropertyDefinition)mi).Parameters;
2713 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
2714 int pcount = pis == null ? 0 : pis.Count;
2715 if (mcount != pcount)
2718 MethodDefinition mDef = mi as MethodDefinition;
2719 if (mDef != null && !mDef.IsConstructor) {
2720 // Casting operators can overload based on return type.
2721 if (returntype != GetReplacedString (
2722 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
2723 typeParams, docTypeParams)) {
2731 for (int i = 0; i < pis.Count; i++) {
2732 string paramType = GetReplacedString (
2733 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
2734 typeParams, docTypeParams);
2735 if (paramType != (string) member.Parameters [i]) {
2740 if (!good) continue;
2748 static string[] GetTypeParameters (string docName)
2750 if (docName [docName.Length-1] != '>')
2752 StringList types = new StringList ();
2753 int endToken = docName.Length-2;
2754 int i = docName.Length-2;
2756 if (docName [i] == ',' || docName [i] == '<') {
2757 types.Add (docName.Substring (i + 1, endToken - i));
2760 if (docName [i] == '<')
2765 return types.ToArray ();
2768 protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
2770 // need to worry about 4 forms of //@MemberName values:
2771 // 1. "Normal" (non-generic) member names: GetEnumerator
2773 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
2774 // - try as-is, and try type.member (due to "kludge" for property
2776 // 3. "Normal" Generic member names: Sort<T> (CSC)
2777 // - need to remove generic parameters --> "Sort"
2778 // 4. Explicitly-implemented interface members for generic interfaces:
2779 // -- System.Collections.Generic.IEnumerable<T>.Current
2780 // - Try as-is, and try type.member, *keeping* the generic parameters.
2781 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
2782 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
2783 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
2784 // this as (1) or (2).
2785 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
2787 foreach (MemberReference mi in type.GetMembers (docName))
2789 if (CountChars (docName, '.') > 0)
2790 // might be a property; try only type.member instead of
2791 // namespace.type.member.
2792 foreach (MemberReference mi in
2793 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
2800 int startLt, startType, startMethod;
2801 startLt = startType = startMethod = -1;
2802 for (int i = 0; i < docName.Length; ++i) {
2803 switch (docName [i]) {
2812 if (numLt == 0 && (i + 1) < docName.Length)
2813 // there's another character in docName, so this <...> sequence is
2814 // probably part of a generic type -- case 4.
2818 startType = startMethod;
2824 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
2826 foreach (MemberReference mi in type.GetMembers (refName))
2830 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
2833 // If we _still_ haven't found it, we've hit another generic naming issue:
2834 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
2835 // explicitly-implemented METHOD names (not properties), e.g.
2836 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
2837 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
2838 // which the XML docs will contain.
2840 // Alas, we can't derive the Mono name from docName, so we need to iterate
2841 // over all member names, convert them into CSC format, and compare... :-(
2844 foreach (MemberReference mi in type.GetMembers ()) {
2845 if (MDocUpdater.GetMemberName (mi) == docName)
2850 static string GetReplacedString (string typeName, string[] from, string[] to)
2854 for (int i = 0; i < from.Length; ++i)
2855 typeName = typeName.Replace (from [i], to [i]);
2859 private static int CountChars (string s, char c)
2862 for (int i = 0; i < s.Length; ++i) {
2870 class EcmaDocumentationEnumerator : DocumentationEnumerator {
2875 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
2878 this.ecmadocs = ecmaDocs;
2881 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2883 HashSet<string> seen = new HashSet<string> ();
2884 return GetDocumentationTypes (assembly, forTypes, seen)
2885 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
2888 new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2891 while (ecmadocs.Read ()) {
2892 switch (ecmadocs.Name) {
2894 if (typeDepth == -1)
2895 typeDepth = ecmadocs.Depth;
2896 if (ecmadocs.NodeType != XmlNodeType.Element)
2898 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
2900 string typename = ecmadocs.GetAttribute ("FullName");
2901 string typename2 = MDocUpdater.GetTypeFileName (typename);
2902 if (forTypes != null &&
2903 forTypes.BinarySearch (typename) < 0 &&
2904 typename != typename2 &&
2905 forTypes.BinarySearch (typename2) < 0)
2908 if ((t = assembly.GetType (typename)) == null &&
2909 (t = assembly.GetType (typename2)) == null)
2911 seen.Add (typename);
2912 if (typename != typename2)
2913 seen.Add (typename2);
2914 Console.WriteLine (" Import: {0}", t.FullName);
2915 if (ecmadocs.Name != "Docs") {
2916 int depth = ecmadocs.Depth;
2917 while (ecmadocs.Read ()) {
2918 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
2922 if (!ecmadocs.IsStartElement ("Docs"))
2923 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
2933 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2935 return GetMembers (basefile, type)
2936 .Concat (base.GetDocumentationMembers (basefile, type));
2939 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
2941 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
2944 if (ecmadocs.IsEmptyElement)
2947 int membersDepth = ecmadocs.Depth;
2949 while (go && ecmadocs.Read ()) {
2950 switch (ecmadocs.Name) {
2952 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
2954 DocumentationMember dm = new DocumentationMember (ecmadocs);
2955 string xp = MDocUpdater.GetXPathForMember (dm);
2956 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
2958 if (oldmember == null) {
2959 m = GetMember (type, dm);
2961 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2962 type.FullName, dm.MemberSignatures ["C#"]);
2963 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
2966 // oldmember lookup may have failed due to type parameter renames.
2968 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
2969 if (oldmember == null) {
2970 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
2971 oldmember = basefile.CreateElement ("Member");
2972 oldmember.SetAttribute ("MemberName", dm.MemberName);
2973 members.AppendChild (oldmember);
2974 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
2975 XmlElement ms = basefile.CreateElement ("MemberSignature");
2976 ms.SetAttribute ("Language", key);
2977 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
2978 oldmember.AppendChild (ms);
2980 oldmember.SetAttribute ("__monodocer-seen__", "true");
2981 Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
2986 m = GetMember (type, new DocumentationMember (oldmember));
2988 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2989 type.FullName, dm.MemberSignatures ["C#"]);
2992 oldmember.SetAttribute ("__monodocer-seen__", "true");
2994 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
2995 if (ecmadocs.Name != "Docs")
2996 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
3001 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
3010 abstract class DocumentationImporter {
3012 public abstract void ImportDocumentation (DocsNodeInfo info);
3015 class MsxdocDocumentationImporter : DocumentationImporter {
3017 XmlDocument slashdocs;
3019 public MsxdocDocumentationImporter (string file)
3021 var xml = File.ReadAllText (file);
3023 // Ensure Unix line endings
3024 xml = xml.Replace ("\r", "");
3026 slashdocs = new XmlDocument();
3027 slashdocs.LoadXml (xml);
3030 public override void ImportDocumentation (DocsNodeInfo info)
3032 XmlNode elem = GetDocs (info.Member ?? info.Type);
3037 XmlElement e = info.Node;
3039 if (elem.SelectSingleNode("summary") != null)
3040 MDocUpdater.ClearElement(e, "summary");
3041 if (elem.SelectSingleNode("remarks") != null)
3042 MDocUpdater.ClearElement(e, "remarks");
3043 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3044 MDocUpdater.ClearElement(e, "value");
3045 MDocUpdater.ClearElement(e, "returns");
3048 foreach (XmlNode child in elem.ChildNodes) {
3049 switch (child.Name) {
3052 XmlAttribute name = child.Attributes ["name"];
3055 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3057 p2.InnerXml = child.InnerXml;
3060 // Occasionally XML documentation will use <returns/> on
3061 // properties, so let's try to normalize things.
3064 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3065 v.InnerXml = child.InnerXml;
3071 case "permission": {
3072 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3075 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3077 a = e.OwnerDocument.CreateElement (child.Name);
3078 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
3081 a.InnerXml = child.InnerXml;
3085 XmlAttribute cref = child.Attributes ["cref"];
3088 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3090 a = e.OwnerDocument.CreateElement ("altmember");
3091 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
3098 if (child.NodeType == XmlNodeType.Element &&
3099 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3102 MDocUpdater.CopyNode (child, e);
3109 private XmlNode GetDocs (MemberReference member)
3111 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3112 if (slashdocsig != null)
3113 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3118 class EcmaDocumentationImporter : DocumentationImporter {
3122 public EcmaDocumentationImporter (XmlReader ecmaDocs)
3124 this.ecmadocs = ecmaDocs;
3127 public override void ImportDocumentation (DocsNodeInfo info)
3129 if (!ecmadocs.IsStartElement ("Docs")) {
3133 XmlElement e = info.Node;
3135 int depth = ecmadocs.Depth;
3136 ecmadocs.ReadStartElement ("Docs");
3137 while (ecmadocs.Read ()) {
3138 if (ecmadocs.Name == "Docs") {
3139 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3142 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3144 if (!ecmadocs.IsStartElement ())
3146 switch (ecmadocs.Name) {
3149 string name = ecmadocs.GetAttribute ("name");
3152 XmlNode doc = e.SelectSingleNode (
3153 ecmadocs.Name + "[@name='" + name + "']");
3154 string value = ecmadocs.ReadInnerXml ();
3156 doc.InnerXml = value.Replace ("\r", "");
3163 string name = ecmadocs.Name;
3164 string cref = ecmadocs.GetAttribute ("cref");
3167 XmlNode doc = e.SelectSingleNode (
3168 ecmadocs.Name + "[@cref='" + cref + "']");
3169 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3171 doc.InnerXml = value;
3173 XmlElement n = e.OwnerDocument.CreateElement (name);
3174 n.SetAttribute ("cref", cref);
3181 string name = ecmadocs.Name;
3182 string xpath = ecmadocs.Name;
3183 StringList attributes = new StringList (ecmadocs.AttributeCount);
3184 if (ecmadocs.MoveToFirstAttribute ()) {
3186 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3187 } while (ecmadocs.MoveToNextAttribute ());
3188 ecmadocs.MoveToContent ();
3190 if (attributes.Count > 0) {
3191 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3193 XmlNode doc = e.SelectSingleNode (xpath);
3194 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3196 doc.InnerXml = value;
3199 XmlElement n = e.OwnerDocument.CreateElement (name);
3201 foreach (string a in attributes) {
3202 int eq = a.IndexOf ('=');
3203 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3214 class DocumentationMember {
3215 public StringToStringMap MemberSignatures = new StringToStringMap ();
3216 public string ReturnType;
3217 public StringList Parameters;
3218 public string MemberName;
3219 public string MemberType;
3221 public DocumentationMember (XmlReader reader)
3223 MemberName = reader.GetAttribute ("MemberName");
3224 int depth = reader.Depth;
3226 StringList p = new StringList ();
3228 if (reader.NodeType != XmlNodeType.Element)
3230 switch (reader.Name) {
3231 case "MemberSignature":
3232 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3235 MemberType = reader.ReadElementString ();
3238 if (reader.Depth == depth + 2)
3239 ReturnType = reader.ReadElementString ();
3242 if (reader.Depth == depth + 2)
3243 p.Add (reader.GetAttribute ("Type"));
3246 if (reader.Depth == depth + 1)
3250 } while (go && reader.Read () && reader.Depth >= depth);
3256 public DocumentationMember (XmlNode node)
3258 MemberName = node.Attributes ["MemberName"].Value;
3259 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3260 XmlAttribute l = n.Attributes ["Language"];
3261 XmlAttribute v = n.Attributes ["Value"];
3262 if (l != null && v != null)
3263 MemberSignatures [l.Value] = v.Value;
3265 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3266 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
3268 ReturnType = rt.InnerText;
3269 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
3271 Parameters = new StringList (p.Count);
3272 for (int i = 0; i < p.Count; ++i)
3273 Parameters.Add (p [i].Attributes ["Type"].Value);
3278 public class DynamicParserContext {
3279 public ReadOnlyCollection<bool> TransformFlags;
3280 public int TransformIndex;
3282 public DynamicParserContext (ICustomAttributeProvider provider)
3285 if (provider.HasCustomAttributes &&
3286 (da = (provider.CustomAttributes.Cast<CustomAttribute>()
3287 .SingleOrDefault (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))) != null) {
3288 CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
3289 ? new CustomAttributeArgument [0]
3290 : (CustomAttributeArgument[]) da.ConstructorArguments [0].Value;
3292 TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool) t.Value).ToArray());
3297 public enum MemberFormatterState {
3299 WithinGenericTypeParameters,
3302 public abstract class MemberFormatter {
3304 public virtual string Language {
3308 public string GetName (MemberReference member)
3310 return GetName (member, null);
3313 public virtual string GetName (MemberReference member, DynamicParserContext context)
3315 TypeReference type = member as TypeReference;
3317 return GetTypeName (type, context);
3318 MethodReference method = member as MethodReference;
3319 if (method != null && method.Name == ".ctor") // method.IsConstructor
3320 return GetConstructorName (method);
3322 return GetMethodName (method);
3323 PropertyReference prop = member as PropertyReference;
3325 return GetPropertyName (prop);
3326 FieldReference field = member as FieldReference;
3328 return GetFieldName (field);
3329 EventReference e = member as EventReference;
3331 return GetEventName (e);
3332 throw new NotSupportedException ("Can't handle: " +
3333 (member == null ? "null" : member.GetType().ToString()));
3336 protected virtual string GetTypeName (TypeReference type)
3338 return GetTypeName (type, null);
3341 protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
3344 throw new ArgumentNullException ("type");
3345 return _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
3348 protected virtual char[] ArrayDelimeters {
3349 get {return new char[]{'[', ']'};}
3352 protected virtual MemberFormatterState MemberFormatterState { get; set; }
3354 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3356 if (type is ArrayType) {
3357 TypeSpecification spec = type as TypeSpecification;
3358 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
3359 return AppendArrayModifiers (buf, (ArrayType) type);
3361 if (type is ByReferenceType) {
3362 return AppendRefTypeName (buf, type, context);
3364 if (type is PointerType) {
3365 return AppendPointerTypeName (buf, type, context);
3367 AppendNamespace (buf, type);
3368 if (type is GenericParameter) {
3369 return AppendTypeName (buf, type, context);
3371 GenericInstanceType genInst = type as GenericInstanceType;
3372 if (type.GenericParameters.Count == 0 &&
3373 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3374 return AppendFullTypeName (buf, type, context);
3376 return AppendGenericType (buf, type, context);
3379 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3381 string ns = DocUtils.GetNamespace (type);
3382 if (ns != null && ns.Length > 0)
3383 buf.Append (ns).Append ('.');
3387 protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3389 if (type.DeclaringType != null)
3390 AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
3391 return AppendTypeName (buf, type, context);
3394 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3396 if (context != null)
3397 context.TransformIndex++;
3398 return AppendTypeName (buf, type.Name);
3401 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3403 int n = typename.IndexOf ("`");
3405 return buf.Append (typename.Substring (0, n));
3406 return buf.Append (typename);
3409 protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
3411 buf.Append (ArrayDelimeters [0]);
3412 int rank = array.Rank;
3414 buf.Append (new string (',', rank-1));
3415 return buf.Append (ArrayDelimeters [1]);
3418 protected virtual string RefTypeModifier {
3422 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3424 TypeSpecification spec = type as TypeSpecification;
3425 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
3426 .Append (RefTypeModifier);
3429 protected virtual string PointerModifier {
3433 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3435 TypeSpecification spec = type as TypeSpecification;
3436 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
3437 .Append (PointerModifier);
3440 protected virtual char[] GenericTypeContainer {
3441 get {return new char[]{'<', '>'};}
3444 protected virtual char NestedTypeSeparator {
3448 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
3450 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3451 type is GenericInstanceType ? type.GetElementType () : type);
3452 List<TypeReference> genArgs = GetGenericArguments (type);
3455 bool insertNested = false;
3456 foreach (var decl in decls) {
3457 TypeReference declDef = decl.Resolve () ?? decl;
3459 buf.Append (NestedTypeSeparator);
3461 insertNested = true;
3462 AppendTypeName (buf, declDef, context);
3463 int ac = DocUtils.GetGenericArgumentCount (declDef);
3467 buf.Append (GenericTypeContainer [0]);
3468 var origState = MemberFormatterState;
3469 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3470 _AppendTypeName (buf, genArgs [argIdx++], context);
3471 for (int i = 1; i < c; ++i) {
3472 _AppendTypeName (buf.Append (","), genArgs [argIdx++], context);
3474 MemberFormatterState = origState;
3475 buf.Append (GenericTypeContainer [1]);
3481 protected List<TypeReference> GetGenericArguments (TypeReference type)
3483 var args = new List<TypeReference> ();
3484 GenericInstanceType inst = type as GenericInstanceType;
3486 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
3488 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
3492 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3497 protected virtual string GetConstructorName (MethodReference constructor)
3499 return constructor.Name;
3502 protected virtual string GetMethodName (MethodReference method)
3507 protected virtual string GetPropertyName (PropertyReference property)
3509 return property.Name;
3512 protected virtual string GetFieldName (FieldReference field)
3517 protected virtual string GetEventName (EventReference e)
3522 public virtual string GetDeclaration (MemberReference member)
3525 throw new ArgumentNullException ("member");
3526 TypeDefinition type = member as TypeDefinition;
3528 return GetTypeDeclaration (type);
3529 MethodDefinition method = member as MethodDefinition;
3530 if (method != null && method.IsConstructor)
3531 return GetConstructorDeclaration (method);
3533 return GetMethodDeclaration (method);
3534 PropertyDefinition prop = member as PropertyDefinition;
3536 return GetPropertyDeclaration (prop);
3537 FieldDefinition field = member as FieldDefinition;
3539 return GetFieldDeclaration (field);
3540 EventDefinition e = member as EventDefinition;
3542 return GetEventDeclaration (e);
3543 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3546 protected virtual string GetTypeDeclaration (TypeDefinition type)
3549 throw new ArgumentNullException ("type");
3550 StringBuilder buf = new StringBuilder (type.Name.Length);
3551 _AppendTypeName (buf, type, null);
3552 AppendGenericTypeConstraints (buf, type);
3553 return buf.ToString ();
3556 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
3558 return GetConstructorName (constructor);
3561 protected virtual string GetMethodDeclaration (MethodDefinition method)
3563 if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
3564 ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
3567 // Special signature for destructors.
3568 if (method.Name == "Finalize" && method.Parameters.Count == 0)
3569 return GetFinalizerName (method);
3571 StringBuilder buf = new StringBuilder ();
3573 AppendVisibility (buf, method);
3574 if (buf.Length == 0 &&
3575 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3578 AppendModifiers (buf, method);
3580 if (buf.Length != 0)
3582 buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
3584 AppendMethodName (buf, method);
3585 AppendGenericMethod (buf, method).Append (" ");
3586 AppendParameters (buf, method, method.Parameters);
3587 AppendGenericMethodConstraints (buf, method);
3588 return buf.ToString ();
3591 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3593 return buf.Append (method.Name);
3596 protected virtual string GetFinalizerName (MethodDefinition method)
3601 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3606 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3611 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3616 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
3621 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3626 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
3628 return GetPropertyName (property);
3631 protected virtual string GetFieldDeclaration (FieldDefinition field)
3633 return GetFieldName (field);
3636 protected virtual string GetEventDeclaration (EventDefinition e)
3638 return GetEventName (e);
3642 class ILFullMemberFormatter : MemberFormatter {
3644 public override string Language {
3645 get {return "ILAsm";}
3648 protected override char NestedTypeSeparator {
3654 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3656 if (GetBuiltinType (type.FullName) != null)
3658 string ns = DocUtils.GetNamespace (type);
3659 if (ns != null && ns.Length > 0) {
3660 if (type.IsValueType)
3661 buf.Append ("valuetype ");
3663 buf.Append ("class ");
3664 buf.Append (ns).Append ('.');
3669 private static string GetBuiltinType (string t)
3672 case "System.Byte": return "unsigned int8";
3673 case "System.SByte": return "int8";
3674 case "System.Int16": return "int16";
3675 case "System.Int32": return "int32";
3676 case "System.Int64": return "int64";
3677 case "System.IntPtr": return "native int";
3679 case "System.UInt16": return "unsigned int16";
3680 case "System.UInt32": return "unsigned int32";
3681 case "System.UInt64": return "unsigned int64";
3682 case "System.UIntPtr": return "native unsigned int";
3684 case "System.Single": return "float32";
3685 case "System.Double": return "float64";
3686 case "System.Boolean": return "bool";
3687 case "System.Char": return "char";
3688 case "System.Void": return "void";
3689 case "System.String": return "string";
3690 case "System.Object": return "object";
3695 protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
3697 return buf.Append (typename);
3700 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3702 if (type is GenericParameter) {
3703 AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
3707 string s = GetBuiltinType (type.FullName);
3709 return buf.Append (s);
3711 return base.AppendTypeName (buf, type, context);
3714 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
3716 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
3717 return buf.Append (type.Owner is TypeReference ? "!" : "!!");
3719 GenericParameterAttributes attrs = type.Attributes;
3720 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
3721 buf.Append ("class ");
3722 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
3723 buf.Append ("struct ");
3724 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
3725 buf.Append (".ctor ");
3726 IList<TypeReference> constraints = type.Constraints;
3727 MemberFormatterState = 0;
3728 if (constraints.Count > 0) {
3729 var full = new ILFullMemberFormatter ();
3730 buf.Append ("(").Append (full.GetName (constraints [0]));
3731 for (int i = 1; i < constraints.Count; ++i) {
3732 buf.Append (", ").Append (full.GetName (constraints [i]));
3736 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3738 if ((attrs & GenericParameterAttributes.Covariant) != 0)
3740 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
3745 protected override string GetTypeDeclaration (TypeDefinition type)
3747 string visibility = GetTypeVisibility (type.Attributes);
3748 if (visibility == null)
3751 StringBuilder buf = new StringBuilder ();
3753 buf.Append (".class ");
3755 buf.Append ("nested ");
3756 buf.Append (visibility).Append (" ");
3757 if (type.IsInterface)
3758 buf.Append ("interface ");
3759 if (type.IsSequentialLayout)
3760 buf.Append ("sequential ");
3761 if (type.IsAutoLayout)
3762 buf.Append ("auto ");
3763 if (type.IsAnsiClass)
3764 buf.Append ("ansi ");
3765 if (type.IsAbstract)
3766 buf.Append ("abstract ");
3767 if (type.IsSerializable)
3768 buf.Append ("serializable ");
3770 buf.Append ("sealed ");
3771 if (type.IsBeforeFieldInit)
3772 buf.Append ("beforefieldinit ");
3773 var state = MemberFormatterState;
3774 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3775 buf.Append (GetName (type));
3776 MemberFormatterState = state;
3777 var full = new ILFullMemberFormatter ();
3778 if (type.BaseType != null) {
3779 buf.Append (" extends ");
3780 if (type.BaseType.FullName == "System.Object")
3781 buf.Append ("System.Object");
3783 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
3786 foreach (var name in type.Interfaces
3787 .Select (i => full.GetName (i))
3788 .OrderBy (n => n)) {
3790 buf.Append (" implements ");
3799 return buf.ToString ();
3802 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
3804 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3805 type is GenericInstanceType ? type.GetElementType () : type);
3807 foreach (var decl in decls) {
3808 TypeReference declDef = decl.Resolve () ?? decl;
3810 buf.Append (NestedTypeSeparator);
3813 AppendTypeName (buf, declDef, context);
3817 foreach (TypeReference arg in GetGenericArguments (type)) {
3821 _AppendTypeName (buf, arg, context);
3827 static string GetTypeVisibility (TypeAttributes ta)
3829 switch (ta & TypeAttributes.VisibilityMask) {
3830 case TypeAttributes.Public:
3831 case TypeAttributes.NestedPublic:
3834 case TypeAttributes.NestedFamily:
3835 case TypeAttributes.NestedFamORAssem:
3843 protected override string GetConstructorDeclaration (MethodDefinition constructor)
3845 return GetMethodDeclaration (constructor);
3848 protected override string GetMethodDeclaration (MethodDefinition method)
3850 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
3853 var buf = new StringBuilder ();
3854 buf.Append (".method ");
3855 AppendVisibility (buf, method);
3856 if (method.IsStatic)
3857 buf.Append ("static ");
3858 if (method.IsHideBySig)
3859 buf.Append ("hidebysig ");
3860 if (method.IsPInvokeImpl) {
3861 var info = method.PInvokeInfo;
3862 buf.Append ("pinvokeimpl (\"")
3863 .Append (info.Module.Name)
3864 .Append ("\" as \"")
3865 .Append (info.EntryPoint)
3867 if (info.IsCharSetAuto)
3868 buf.Append (" auto");
3869 if (info.IsCharSetUnicode)
3870 buf.Append (" unicode");
3871 if (info.IsCharSetAnsi)
3872 buf.Append (" ansi");
3873 if (info.IsCallConvCdecl)
3874 buf.Append (" cdecl");
3875 if (info.IsCallConvStdCall)
3876 buf.Append (" stdcall");
3877 if (info.IsCallConvWinapi)
3878 buf.Append (" winapi");
3879 if (info.IsCallConvThiscall)
3880 buf.Append (" thiscall");
3881 if (info.SupportsLastError)
3882 buf.Append (" lasterr");
3885 if (method.IsSpecialName)
3886 buf.Append ("specialname ");
3887 if (method.IsRuntimeSpecialName)
3888 buf.Append ("rtspecialname ");
3889 if (method.IsNewSlot)
3890 buf.Append ("newslot ");
3891 if (method.IsVirtual)
3892 buf.Append ("virtual ");
3893 if (!method.IsStatic)
3894 buf.Append ("instance ");
3895 _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
3897 .Append (method.Name);
3898 if (method.IsGenericMethod ()) {
3899 var state = MemberFormatterState;
3900 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3901 IList<GenericParameter> args = method.GenericParameters;
3902 if (args.Count > 0) {
3904 _AppendTypeName (buf, args [0], null);
3905 for (int i = 1; i < args.Count; ++i)
3906 _AppendTypeName (buf.Append (", "), args [i], null);
3909 MemberFormatterState = state;
3914 for (int i = 0; i < method.Parameters.Count; ++i) {
3918 _AppendTypeName (buf, method.Parameters [i].ParameterType, new DynamicParserContext (method.Parameters [i]));
3920 buf.Append (method.Parameters [i].Name);
3924 buf.Append (" cil");
3925 if (method.IsRuntime)
3926 buf.Append (" runtime");
3927 if (method.IsManaged)
3928 buf.Append (" managed");
3930 return buf.ToString ();
3933 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3935 if (DocUtils.IsExplicitlyImplemented (method)) {
3936 TypeReference iface;
3937 MethodReference ifaceMethod;
3938 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3939 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3941 .Append (ifaceMethod.Name);
3943 return base.AppendMethodName (buf, method);
3946 protected override string RefTypeModifier {
3950 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3952 if (method.IsPublic)
3953 return buf.Append ("public ");
3954 if (method.IsFamilyAndAssembly)
3955 return buf.Append ("familyandassembly");
3956 if (method.IsFamilyOrAssembly)
3957 return buf.Append ("familyorassembly");
3958 if (method.IsFamily)
3959 return buf.Append ("family");
3963 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3965 string modifiers = String.Empty;
3966 if (method.IsStatic) modifiers += " static";
3967 if (method.IsVirtual && !method.IsAbstract) {
3968 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3969 else modifiers += " override";
3971 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
3972 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
3973 if (method.IsFinal) modifiers += " sealed";
3974 if (modifiers == " virtual sealed") modifiers = "";
3976 return buf.Append (modifiers);
3979 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3981 if (method.IsGenericMethod ()) {
3982 IList<GenericParameter> args = method.GenericParameters;
3983 if (args.Count > 0) {
3985 buf.Append (args [0].Name);
3986 for (int i = 1; i < args.Count; ++i)
3987 buf.Append (",").Append (args [i].Name);
3994 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
3996 return AppendParameters (buf, method, parameters, '(', ')');
3999 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4003 if (parameters.Count > 0) {
4004 if (DocUtils.IsExtensionMethod (method))
4005 buf.Append ("this ");
4006 AppendParameter (buf, parameters [0]);
4007 for (int i = 1; i < parameters.Count; ++i) {
4009 AppendParameter (buf, parameters [i]);
4013 return buf.Append (end);
4016 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4018 if (parameter.ParameterType is ByReferenceType) {
4019 if (parameter.IsOut)
4020 buf.Append ("out ");
4022 buf.Append ("ref ");
4024 buf.Append (GetName (parameter.ParameterType)).Append (" ");
4025 return buf.Append (parameter.Name);
4028 protected override string GetPropertyDeclaration (PropertyDefinition property)
4030 MethodDefinition gm = null, sm = null;
4032 string get_visible = null;
4033 if ((gm = property.GetMethod) != null &&
4034 (DocUtils.IsExplicitlyImplemented (gm) ||
4035 (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
4036 get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
4037 string set_visible = null;
4038 if ((sm = property.SetMethod) != null &&
4039 (DocUtils.IsExplicitlyImplemented (sm) ||
4040 (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
4041 set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
4043 if ((set_visible == null) && (get_visible == null))
4046 StringBuilder buf = new StringBuilder ()
4047 .Append (".property ");
4048 if (!(gm ?? sm).IsStatic)
4049 buf.Append ("instance ");
4050 _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
4051 buf.Append (' ').Append (property.Name);
4052 if (!property.HasParameters || property.Parameters.Count == 0)
4053 return buf.ToString ();
4057 foreach (ParameterDefinition p in property.Parameters) {
4061 _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
4065 return buf.ToString ();
4068 protected override string GetFieldDeclaration (FieldDefinition field)
4070 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4071 if (declType.IsEnum && field.Name == "value__")
4072 return null; // This member of enums aren't documented.
4074 StringBuilder buf = new StringBuilder ();
4075 AppendFieldVisibility (buf, field);
4076 if (buf.Length == 0)
4079 buf.Insert (0, ".field ");
4082 buf.Append ("static ");
4083 if (field.IsInitOnly)
4084 buf.Append ("initonly ");
4085 if (field.IsLiteral)
4086 buf.Append ("literal ");
4087 _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
4088 buf.Append (' ').Append (field.Name);
4089 AppendFieldValue (buf, field);
4091 return buf.ToString ();
4094 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4097 return buf.Append ("public ");
4098 if (field.IsFamilyAndAssembly)
4099 return buf.Append ("familyandassembly ");
4100 if (field.IsFamilyOrAssembly)
4101 return buf.Append ("familyorassembly ");
4103 return buf.Append ("family ");
4107 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4109 // enums have a value__ field, which we ignore
4110 if (field.DeclaringType.IsGenericType ())
4112 if (field.HasConstant && field.IsLiteral) {
4115 val = field.Constant;
4120 buf.Append (" = ").Append ("null");
4121 else if (val is Enum)
4123 .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4125 .Append (val.ToString ())
4127 else if (val is IFormattable) {
4128 string value = ((IFormattable)val).ToString();
4131 buf.Append ("\"" + value + "\"");
4133 buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4142 protected override string GetEventDeclaration (EventDefinition e)
4144 StringBuilder buf = new StringBuilder ();
4145 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4150 buf.Append (".event ")
4151 .Append (GetName (e.EventType))
4155 return buf.ToString ();
4159 class ILMemberFormatter : ILFullMemberFormatter {
4160 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4166 class CSharpFullMemberFormatter : MemberFormatter {
4168 public override string Language {
4172 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4174 string ns = DocUtils.GetNamespace (type);
4175 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
4176 buf.Append (ns).Append ('.');
4180 private string GetCSharpType (string t)
4183 case "System.Byte": return "byte";
4184 case "System.SByte": return "sbyte";
4185 case "System.Int16": return "short";
4186 case "System.Int32": return "int";
4187 case "System.Int64": return "long";
4189 case "System.UInt16": return "ushort";
4190 case "System.UInt32": return "uint";
4191 case "System.UInt64": return "ulong";
4193 case "System.Single": return "float";
4194 case "System.Double": return "double";
4195 case "System.Decimal": return "decimal";
4196 case "System.Boolean": return "bool";
4197 case "System.Char": return "char";
4198 case "System.Void": return "void";
4199 case "System.String": return "string";
4200 case "System.Object": return "object";
4205 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4207 if (context != null && context.TransformFlags != null &&
4208 (context.TransformFlags.Count == 0 || context.TransformFlags [context.TransformIndex])) {
4209 context.TransformIndex++;
4210 return buf.Append ("dynamic");
4213 if (type is GenericParameter)
4214 return AppendGenericParameterConstraints (buf, (GenericParameter) type, context).Append (type.Name);
4215 string t = type.FullName;
4216 if (!t.StartsWith ("System.")) {
4217 return base.AppendTypeName (buf, type, context);
4220 string s = GetCSharpType (t);
4222 if (context != null)
4223 context.TransformIndex++;
4224 return buf.Append (s);
4227 return base.AppendTypeName (buf, type, context);
4230 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
4232 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
4234 GenericParameterAttributes attrs = type.Attributes;
4235 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
4236 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
4240 buf.Append ("out ");
4244 protected override string GetTypeDeclaration (TypeDefinition type)
4246 string visibility = GetTypeVisibility (type.Attributes);
4247 if (visibility == null)
4250 StringBuilder buf = new StringBuilder ();
4252 buf.Append (visibility);
4255 MemberFormatter full = new CSharpFullMemberFormatter ();
4257 if (DocUtils.IsDelegate (type)) {
4258 buf.Append("delegate ");
4259 MethodDefinition invoke = type.GetMethod ("Invoke");
4260 buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
4261 buf.Append (GetName (type));
4262 AppendParameters (buf, invoke, invoke.Parameters);
4263 AppendGenericTypeConstraints (buf, type);
4266 return buf.ToString();
4269 if (type.IsAbstract && !type.IsInterface)
4270 buf.Append("abstract ");
4271 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
4272 buf.Append("sealed ");
4273 buf.Replace ("abstract sealed", "static");
4275 buf.Append (GetTypeKind (type));
4277 buf.Append (GetCSharpType (type.FullName) == null
4282 TypeReference basetype = type.BaseType;
4283 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
4286 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
4287 .Select (iface => full.GetName (iface))
4291 if (basetype != null || interface_names.Count > 0)
4294 if (basetype != null) {
4295 buf.Append (full.GetName (basetype));
4296 if (interface_names.Count > 0)
4300 for (int i = 0; i < interface_names.Count; i++){
4303 buf.Append (interface_names [i]);
4305 AppendGenericTypeConstraints (buf, type);
4308 return buf.ToString ();
4311 static string GetTypeKind (TypeDefinition t)
4317 if (t.IsClass || t.FullName == "System.Enum")
4321 throw new ArgumentException(t.FullName);
4324 static string GetTypeVisibility (TypeAttributes ta)
4326 switch (ta & TypeAttributes.VisibilityMask) {
4327 case TypeAttributes.Public:
4328 case TypeAttributes.NestedPublic:
4331 case TypeAttributes.NestedFamily:
4332 case TypeAttributes.NestedFamORAssem:
4340 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4342 if (type.GenericParameters.Count == 0)
4344 return AppendConstraints (buf, type.GenericParameters);
4347 private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
4349 foreach (GenericParameter genArg in genArgs) {
4350 GenericParameterAttributes attrs = genArg.Attributes;
4351 IList<TypeReference> constraints = genArg.Constraints;
4352 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
4355 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
4356 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
4357 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
4360 if (!isref && !isvt && !isnew && constraints.Count == 0)
4362 buf.Append (" where ").Append (genArg.Name).Append (" : ");
4364 buf.Append ("class");
4368 buf.Append ("struct");
4371 if (constraints.Count > 0 && !isvt) {
4374 buf.Append (GetTypeName (constraints [0]));
4375 for (int i = 1; i < constraints.Count; ++i)
4376 buf.Append (", ").Append (GetTypeName (constraints [i]));
4378 if (isnew && !isvt) {
4381 buf.Append ("new()");
4387 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4389 StringBuilder buf = new StringBuilder ();
4390 AppendVisibility (buf, constructor);
4391 if (buf.Length == 0)
4395 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
4396 AppendParameters (buf, constructor, constructor.Parameters);
4399 return buf.ToString ();
4402 protected override string GetMethodDeclaration (MethodDefinition method)
4404 string decl = base.GetMethodDeclaration (method);
4410 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4412 if (DocUtils.IsExplicitlyImplemented (method)) {
4413 TypeReference iface;
4414 MethodReference ifaceMethod;
4415 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4416 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4418 .Append (ifaceMethod.Name);
4420 return base.AppendMethodName (buf, method);
4423 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4425 if (method.GenericParameters.Count == 0)
4427 return AppendConstraints (buf, method.GenericParameters);
4430 protected override string RefTypeModifier {
4434 protected override string GetFinalizerName (MethodDefinition method)
4436 return "~" + method.DeclaringType.Name + " ()";
4439 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4443 if (method.IsPublic)
4444 return buf.Append ("public");
4445 if (method.IsFamily || method.IsFamilyOrAssembly)
4446 return buf.Append ("protected");
4450 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4452 string modifiers = String.Empty;
4453 if (method.IsStatic) modifiers += " static";
4454 if (method.IsVirtual && !method.IsAbstract) {
4455 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4456 else modifiers += " override";
4458 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4459 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4460 if (method.IsFinal) modifiers += " sealed";
4461 if (modifiers == " virtual sealed") modifiers = "";
4463 return buf.Append (modifiers);
4466 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4468 if (method.IsGenericMethod ()) {
4469 IList<GenericParameter> args = method.GenericParameters;
4470 if (args.Count > 0) {
4472 buf.Append (args [0].Name);
4473 for (int i = 1; i < args.Count; ++i)
4474 buf.Append (",").Append (args [i].Name);
4481 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4483 return AppendParameters (buf, method, parameters, '(', ')');
4486 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4490 if (parameters.Count > 0) {
4491 if (DocUtils.IsExtensionMethod (method))
4492 buf.Append ("this ");
4493 AppendParameter (buf, parameters [0]);
4494 for (int i = 1; i < parameters.Count; ++i) {
4496 AppendParameter (buf, parameters [i]);
4500 return buf.Append (end);
4503 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4505 if (parameter.ParameterType is ByReferenceType) {
4506 if (parameter.IsOut)
4507 buf.Append ("out ");
4509 buf.Append ("ref ");
4511 buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
4512 buf.Append (parameter.Name);
4513 if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant) {
4514 buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
4519 protected override string GetPropertyDeclaration (PropertyDefinition property)
4521 MethodDefinition method;
4523 string get_visible = null;
4524 if ((method = property.GetMethod) != null &&
4525 (DocUtils.IsExplicitlyImplemented (method) ||
4526 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
4527 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
4528 string set_visible = null;
4529 if ((method = property.SetMethod) != null &&
4530 (DocUtils.IsExplicitlyImplemented (method) ||
4531 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
4532 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
4534 if ((set_visible == null) && (get_visible == null))
4538 StringBuilder buf = new StringBuilder ();
4539 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
4540 buf.Append (visibility = get_visible);
4541 else if (set_visible != null && get_visible == null)
4542 buf.Append (visibility = set_visible);
4544 buf.Append (visibility = "public");
4546 // Pick an accessor to use for static/virtual/override/etc. checks.
4547 method = property.SetMethod;
4549 method = property.GetMethod;
4551 string modifiers = String.Empty;
4552 if (method.IsStatic) modifiers += " static";
4553 if (method.IsVirtual && !method.IsAbstract) {
4554 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
4555 modifiers += " virtual";
4557 modifiers += " override";
4559 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
4560 if (method.IsAbstract && !declDef.IsInterface)
4561 modifiers += " abstract";
4563 modifiers += " sealed";
4564 if (modifiers == " virtual sealed")
4566 buf.Append (modifiers).Append (' ');
4568 buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
4570 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
4571 string name = property.Name;
4572 foreach (MemberReference mi in defs) {
4573 if (mi == property) {
4578 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
4580 if (property.Parameters.Count != 0) {
4581 AppendParameters (buf, method, property.Parameters, '[', ']');
4585 if (get_visible != null) {
4586 if (get_visible != visibility)
4587 buf.Append (' ').Append (get_visible);
4588 buf.Append (" get;");
4590 if (set_visible != null) {
4591 if (set_visible != visibility)
4592 buf.Append (' ').Append (set_visible);
4593 buf.Append (" set;");
4597 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
4600 protected override string GetFieldDeclaration (FieldDefinition field)
4602 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4603 if (declType.IsEnum && field.Name == "value__")
4604 return null; // This member of enums aren't documented.
4606 StringBuilder buf = new StringBuilder ();
4607 AppendFieldVisibility (buf, field);
4608 if (buf.Length == 0)
4611 if (declType.IsEnum)
4614 if (field.IsStatic && !field.IsLiteral)
4615 buf.Append (" static");
4616 if (field.IsInitOnly)
4617 buf.Append (" readonly");
4618 if (field.IsLiteral)
4619 buf.Append (" const");
4621 buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
4622 buf.Append (field.Name);
4623 AppendFieldValue (buf, field);
4626 return buf.ToString ();
4629 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4632 return buf.Append ("public");
4633 if (field.IsFamily || field.IsFamilyOrAssembly)
4634 return buf.Append ("protected");
4638 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4640 // enums have a value__ field, which we ignore
4641 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
4642 field.DeclaringType.IsGenericType ())
4644 if (field.HasConstant && field.IsLiteral) {
4647 val = field.Constant;
4652 buf.Append (" = ").Append ("null");
4653 else if (val is Enum)
4654 buf.Append (" = ").Append (val.ToString ());
4655 else if (val is IFormattable) {
4656 string value = ((IFormattable)val).ToString();
4658 value = "\"" + value + "\"";
4659 buf.Append (" = ").Append (value);
4665 protected override string GetEventDeclaration (EventDefinition e)
4667 StringBuilder buf = new StringBuilder ();
4668 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4672 AppendModifiers (buf, e.AddMethod);
4674 buf.Append (" event ");
4675 buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters [0]))).Append (' ');
4676 buf.Append (e.Name).Append (';');
4678 return buf.ToString ();
4682 class CSharpMemberFormatter : CSharpFullMemberFormatter {
4683 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4689 class DocTypeFullMemberFormatter : MemberFormatter {
4690 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
4692 protected override char NestedTypeSeparator {
4697 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
4698 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4704 class SlashDocMemberFormatter : MemberFormatter {
4706 protected override char[] GenericTypeContainer {
4707 get {return new char[]{'{', '}'};}
4710 private bool AddTypeCount = true;
4712 private TypeReference genDeclType;
4713 private MethodReference genDeclMethod;
4715 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4717 if (type is GenericParameter) {
4719 if (genDeclType != null) {
4720 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
4721 for (int i = 0; i < genArgs.Count; ++i) {
4722 if (genArgs [i].Name == type.Name) {
4723 buf.Append ('`').Append (i);
4728 if (genDeclMethod != null) {
4729 IList<GenericParameter> genArgs = null;
4730 if (genDeclMethod.IsGenericMethod ()) {
4731 genArgs = genDeclMethod.GenericParameters;
4732 for (int i = 0; i < genArgs.Count; ++i) {
4733 if (genArgs [i].Name == type.Name) {
4734 buf.Append ("``").Append (i);
4740 if (genDeclType == null && genDeclMethod == null) {
4741 // Probably from within an explicitly implemented interface member,
4742 // where CSC uses parameter names instead of indices (why?), e.g.
4743 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
4744 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
4745 buf.Append (type.Name);
4747 if (buf.Length == l) {
4748 throw new Exception (string.Format (
4749 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
4750 type.Name, genDeclType, genDeclMethod));
4754 base.AppendTypeName (buf, type, context);
4756 int numArgs = type.GenericParameters.Count;
4757 if (type.DeclaringType != null)
4758 numArgs -= type.GenericParameters.Count;
4760 buf.Append ('`').Append (numArgs);
4767 protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
4769 buf.Append (ArrayDelimeters [0]);
4770 int rank = array.Rank;
4773 for (int i = 1; i < rank; ++i) {
4777 return buf.Append (ArrayDelimeters [1]);
4780 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4783 base.AppendGenericType (buf, type, context);
4785 AppendType (buf, type, context);
4789 private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4791 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
4792 bool insertNested = false;
4793 int prevParamCount = 0;
4794 foreach (var decl in decls) {
4796 buf.Append (NestedTypeSeparator);
4797 insertNested = true;
4798 base.AppendTypeName (buf, decl, context);
4799 int argCount = DocUtils.GetGenericArgumentCount (decl);
4800 int numArgs = argCount - prevParamCount;
4801 prevParamCount = argCount;
4803 buf.Append ('`').Append (numArgs);
4808 public override string GetDeclaration (MemberReference member)
4810 TypeReference r = member as TypeReference;
4812 return "T:" + GetTypeName (r);
4814 return base.GetDeclaration (member);
4817 protected override string GetConstructorName (MethodReference constructor)
4819 return GetMethodDefinitionName (constructor, "#ctor");
4822 protected override string GetMethodName (MethodReference method)
4825 MethodDefinition methodDef = method as MethodDefinition;
4826 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
4829 TypeReference iface;
4830 MethodReference ifaceMethod;
4831 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
4832 AddTypeCount = false;
4833 name = GetTypeName (iface) + "." + ifaceMethod.Name;
4834 AddTypeCount = true;
4836 return GetMethodDefinitionName (method, name);
4839 private string GetMethodDefinitionName (MethodReference method, string name)
4841 StringBuilder buf = new StringBuilder ();
4842 buf.Append (GetTypeName (method.DeclaringType));
4844 buf.Append (name.Replace (".", "#"));
4845 if (method.IsGenericMethod ()) {
4846 IList<GenericParameter> genArgs = method.GenericParameters;
4847 if (genArgs.Count > 0)
4848 buf.Append ("``").Append (genArgs.Count);
4850 IList<ParameterDefinition> parameters = method.Parameters;
4852 genDeclType = method.DeclaringType;
4853 genDeclMethod = method;
4854 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
4858 genDeclMethod = null;
4860 return buf.ToString ();
4863 private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
4865 if (parameters.Count == 0)
4870 AppendParameter (buf, genArgs, parameters [0]);
4871 for (int i = 1; i < parameters.Count; ++i) {
4873 AppendParameter (buf, genArgs, parameters [i]);
4876 return buf.Append (')');
4879 private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
4881 AddTypeCount = false;
4882 buf.Append (GetTypeName (parameter.ParameterType));
4883 AddTypeCount = true;
4887 protected override string GetPropertyName (PropertyReference property)
4891 PropertyDefinition propertyDef = property as PropertyDefinition;
4892 MethodDefinition method = null;
4893 if (propertyDef != null)
4894 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
4895 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
4896 name = property.Name;
4898 TypeReference iface;
4899 MethodReference ifaceMethod;
4900 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4901 AddTypeCount = false;
4902 name = string.Join ("#", new string[]{
4903 GetTypeName (iface).Replace (".", "#"),
4904 DocUtils.GetMember (property.Name)
4906 AddTypeCount = true;
4909 StringBuilder buf = new StringBuilder ();
4910 buf.Append (GetName (property.DeclaringType));
4913 IList<ParameterDefinition> parameters = property.Parameters;
4914 if (parameters.Count > 0) {
4915 genDeclType = property.DeclaringType;
4917 IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
4918 AppendParameter (buf, genArgs, parameters [0]);
4919 for (int i = 1; i < parameters.Count; ++i) {
4921 AppendParameter (buf, genArgs, parameters [i]);
4926 return buf.ToString ();
4929 protected override string GetFieldName (FieldReference field)
4931 return string.Format ("{0}.{1}",
4932 GetName (field.DeclaringType), field.Name);
4935 protected override string GetEventName (EventReference e)
4937 return string.Format ("{0}.{1}",
4938 GetName (e.DeclaringType), e.Name);
4941 protected override string GetTypeDeclaration (TypeDefinition type)
4943 string name = GetName (type);
4949 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4951 string name = GetName (constructor);
4957 protected override string GetMethodDeclaration (MethodDefinition method)
4959 string name = GetName (method);
4962 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4963 genDeclType = method.DeclaringType;
4964 genDeclMethod = method;
4965 name += "~" + GetName (method.ReturnType);
4967 genDeclMethod = null;
4972 protected override string GetPropertyDeclaration (PropertyDefinition property)
4974 string name = GetName (property);
4980 protected override string GetFieldDeclaration (FieldDefinition field)
4982 string name = GetName (field);
4988 protected override string GetEventDeclaration (EventDefinition e)
4990 string name = GetName (e);
4997 class FileNameMemberFormatter : SlashDocMemberFormatter {
4998 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5003 protected override char NestedTypeSeparator {