1 // Updater program for syncing Mono's ECMA-style documentation files
3 // By Joshua Tauberer <tauberer@for.net>
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Collections.ObjectModel;
9 using System.Diagnostics;
10 using System.Globalization;
15 using System.Xml.XPath;
20 using MyXmlNodeList = System.Collections.Generic.List<System.Xml.XmlNode>;
21 using StringList = System.Collections.Generic.List<string>;
22 using StringToStringMap = System.Collections.Generic.Dictionary<string, string>;
23 using StringToXmlNodeMap = System.Collections.Generic.Dictionary<string, System.Xml.XmlNode>;
25 namespace Mono.Documentation {
27 class MDocUpdater : MDocCommand
30 List<AssemblyDefinition> assemblies;
31 readonly DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver();
35 bool no_assembly_versions, ignore_missing_types;
36 ExceptionLocations? exceptions;
38 internal int additions = 0, deletions = 0;
40 List<DocumentationImporter> importers = new List<DocumentationImporter> ();
42 DocumentationEnumerator docEnum;
46 static readonly MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
47 static readonly MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
49 static MemberFormatter[] typeFormatters = new MemberFormatter[]{
50 new CSharpMemberFormatter (),
51 new ILMemberFormatter (),
54 static MemberFormatter[] memberFormatters = new MemberFormatter[]{
55 new CSharpFullMemberFormatter (),
56 new ILFullMemberFormatter (),
59 internal static readonly MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
61 MyXmlNodeList extensionMethods = new MyXmlNodeList ();
63 public override void Run (IEnumerable<string> args)
65 show_exceptions = DebugOutput;
66 var types = new List<string> ();
67 var p = new OptionSet () {
69 "Delete removed members from the XML files.",
70 v => delete = v != null },
72 "Document potential exceptions that members can generate. {SOURCES} " +
73 "is a comma-separated list of:\n" +
74 " asm Method calls in same assembly\n" +
75 " depasm Method calls in dependent assemblies\n" +
76 " all Record all possible exceptions\n" +
77 " added Modifier; only create <exception/>s\n" +
78 " for NEW types/members\n" +
79 "If nothing is specified, then only exceptions from the member will " +
81 v => exceptions = ParseExceptionLocations (v) },
83 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
86 case "ignore-missing-types":
87 ignore_missing_types = true;
89 case "no-assembly-versions":
90 no_assembly_versions = true;
93 throw new Exception ("Unsupported flag `" + v + "'.");
96 { "fignore-missing-types",
97 "Do not report an error if a --type=TYPE type\nwas not found.",
98 v => ignore_missing_types = v != null },
99 { "fno-assembly-versions",
100 "Do not generate //AssemblyVersion elements.",
101 v => no_assembly_versions = v != null },
103 "Import documentation from {FILE}.",
104 v => AddImporter (v) },
106 "Check for assembly references in {DIRECTORY}.",
107 v => assemblyResolver.AddSearchDirectory (v) },
109 "Ignored for compatibility with update-ecma-xml.",
112 "Root {DIRECTORY} to generate/update documentation.",
115 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
116 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
117 v => assemblyResolver.AddSearchDirectory (Path.GetDirectoryName (v)) },
119 "Manually specify the assembly {VERSION} that new members were added in.",
122 "Only update documentation for {TYPE}.",
123 v => types.Add (v) },
125 var assemblies = Parse (p, args, "update",
126 "[OPTIONS]+ ASSEMBLIES",
127 "Create or update documentation from ASSEMBLIES.");
128 if (assemblies == null)
130 if (assemblies.Count == 0)
131 Error ("No assemblies specified.");
133 foreach (var dir in assemblies
134 .Where (a => a.Contains (Path.DirectorySeparatorChar))
135 .Select (a => Path.GetDirectoryName (a)))
136 assemblyResolver.AddSearchDirectory (dir);
138 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
141 throw new InvalidOperationException("The --out option is required.");
143 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
145 docEnum = docEnum ?? new DocumentationEnumerator ();
147 // PERFORM THE UPDATES
149 if (types.Count > 0) {
151 DoUpdateTypes (srcPath, types, srcPath);
154 else if (opts.@namespace != null)
155 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
156 Path.Combine (dest_dir, opts.@namespace));
159 DoUpdateAssemblies (srcPath, srcPath);
161 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
164 void AddImporter (string path)
167 XmlReader r = new XmlTextReader (path);
169 while (r.NodeType != XmlNodeType.Element) {
171 Error ("Unable to read XML file: {0}.", path);
173 if (r.LocalName == "doc") {
174 importers.Add (new MsxdocDocumentationImporter (path));
176 else if (r.LocalName == "Libraries") {
177 var ecmadocs = new XmlTextReader (path);
178 docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
179 importers.Add (new EcmaDocumentationImporter (ecmadocs));
182 Error ("Unsupported XML format within {0}.", path);
185 } catch (Exception e) {
186 Environment.ExitCode = 1;
187 Error ("Could not load XML file: {0}.", e.Message);
191 static ExceptionLocations ParseExceptionLocations (string s)
193 ExceptionLocations loc = ExceptionLocations.Member;
196 foreach (var type in s.Split (',')) {
198 case "added": loc |= ExceptionLocations.AddedMembers; break;
199 case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
200 case "asm": loc |= ExceptionLocations.Assembly; break;
201 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
202 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
208 internal void Warning (string format, params object[] args)
210 Message (TraceLevel.Warning, "mdoc: " + format, args);
213 private AssemblyDefinition LoadAssembly (string name)
215 AssemblyDefinition assembly = null;
217 assembly = AssemblyDefinition.ReadAssembly (name, new ReaderParameters { AssemblyResolver = assemblyResolver });
218 } catch (System.IO.FileNotFoundException) { }
220 if (assembly == null)
221 throw new InvalidOperationException("Assembly " + name + " not found.");
226 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
227 OrderTypeAttributes (element);
228 XmlTextWriter writer = new XmlTextWriter(output);
229 writer.Formatting = Formatting.Indented;
230 writer.Indentation = 2;
231 writer.IndentChar = ' ';
232 element.WriteTo(writer);
236 private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
238 Action<string> creator = file => {
239 using (var writer = OpenWrite (file, mode))
243 MdocFile.UpdateFile (filename, creator);
246 private static void OrderTypeAttributes (XmlElement e)
248 foreach (XmlElement type in e.SelectNodes ("//Type")) {
249 OrderTypeAttributes (type.Attributes);
253 static readonly string[] TypeAttributeOrder = {
254 "Name", "FullName", "FullNameSP", "Maintainer"
257 private static void OrderTypeAttributes (XmlAttributeCollection c)
259 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
260 for (int i = 0; i < c.Count; ++i) {
261 XmlAttribute a = c [i];
262 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
263 if (a.Name == TypeAttributeOrder [j]) {
269 for (int i = attrs.Length-1; i >= 0; --i) {
270 XmlAttribute n = attrs [i];
273 XmlAttribute r = null;
274 for (int j = i+1; j < attrs.Length; ++j) {
275 if (attrs [j] != null) {
283 c.InsertBefore (n, r);
287 private XmlDocument CreateIndexStub()
289 XmlDocument index = new XmlDocument();
291 XmlElement index_root = index.CreateElement("Overview");
292 index.AppendChild(index_root);
294 if (assemblies.Count == 0)
295 throw new Exception ("No assembly");
297 XmlElement index_assemblies = index.CreateElement("Assemblies");
298 index_root.AppendChild(index_assemblies);
300 XmlElement index_remarks = index.CreateElement("Remarks");
301 index_remarks.InnerText = "To be added.";
302 index_root.AppendChild(index_remarks);
304 XmlElement index_copyright = index.CreateElement("Copyright");
305 index_copyright.InnerText = "To be added.";
306 index_root.AppendChild(index_copyright);
308 XmlElement index_types = index.CreateElement("Types");
309 index_root.AppendChild(index_types);
314 private static void WriteNamespaceStub(string ns, string outdir) {
315 XmlDocument index = new XmlDocument();
317 XmlElement index_root = index.CreateElement("Namespace");
318 index.AppendChild(index_root);
320 index_root.SetAttribute("Name", ns);
322 XmlElement index_docs = index.CreateElement("Docs");
323 index_root.AppendChild(index_docs);
325 XmlElement index_summary = index.CreateElement("summary");
326 index_summary.InnerText = "To be added.";
327 index_docs.AppendChild(index_summary);
329 XmlElement index_remarks = index.CreateElement("remarks");
330 index_remarks.InnerText = "To be added.";
331 index_docs.AppendChild(index_remarks);
333 WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
334 writer => WriteXml (index.DocumentElement, writer));
337 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
339 var index = CreateIndexForTypes (dest);
341 var found = new HashSet<string> ();
342 foreach (AssemblyDefinition assembly in assemblies) {
343 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames)) {
344 string relpath = DoUpdateType (type, basepath, dest);
348 found.Add (type.FullName);
353 index.Add (assembly);
361 if (ignore_missing_types)
364 var notFound = from n in typenames where !found.Contains (n) select n;
366 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
369 class IndexForTypes {
375 XmlElement index_types;
376 XmlElement index_assemblies;
378 public IndexForTypes (MDocUpdater app, string indexFile, XmlDocument index)
381 this.indexFile = indexFile;
384 index_types = WriteElement (index.DocumentElement, "Types");
385 index_assemblies = WriteElement (index.DocumentElement, "Assemblies");
388 public void Add (AssemblyDefinition assembly)
390 if (index_assemblies.SelectSingleNode ("Assembly[@Name='" + assembly.Name.Name + "']") != null)
393 app.AddIndexAssembly (assembly, index_assemblies);
396 public void Add (TypeDefinition type)
398 app.AddIndexType (type, index_types);
403 SortIndexEntries (index_types);
404 WriteFile (indexFile, FileMode.Create,
405 writer => WriteXml (index.DocumentElement, writer));
409 IndexForTypes CreateIndexForTypes (string dest)
411 string indexFile = Path.Combine (dest, "index.xml");
412 if (File.Exists (indexFile))
414 return new IndexForTypes (this, indexFile, CreateIndexStub ());
417 public string DoUpdateType (TypeDefinition type, string basepath, string dest)
419 if (type.Namespace == null)
420 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
422 if (!IsPublic (type))
425 // Must get the A+B form of the type name.
426 string typename = GetTypeFileName(type);
428 string reltypefile = DocUtils.PathCombine (DocUtils.GetNamespace (type), typename + ".xml");
429 string typefile = Path.Combine (basepath, reltypefile);
430 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
432 string output = null;
435 } else if (dest == "-") {
438 output = Path.Combine (dest, reltypefile);
443 XmlDocument basefile = new XmlDocument();
445 basefile.Load(typefile);
446 } catch (Exception e) {
447 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
450 DoUpdateType2("Updating", basefile, type, output, false);
453 XmlElement td = StubType(type, output);
457 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
460 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
466 public void DoUpdateNS (string ns, string nspath, string outpath)
468 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
469 AssemblyDefinition assembly = assemblies [0];
471 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
472 XmlDocument basefile = new XmlDocument();
473 string typefile = Path.Combine(nspath, file.Name);
475 basefile.Load(typefile);
476 } catch (Exception e) {
477 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
481 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
482 TypeDefinition type = assembly.GetType(typename);
484 Warning ("Type no longer in assembly: " + typename);
488 seenTypes[type] = seenTypes;
489 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false);
492 // Stub types not in the directory
493 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
494 if (type.Namespace != ns || seenTypes.ContainsKey(type))
497 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"));
498 if (td == null) continue;
502 private static string GetTypeFileName (TypeReference type)
504 return filenameFormatter.GetName (type);
507 public static string GetTypeFileName (string typename)
509 StringBuilder filename = new StringBuilder (typename.Length);
513 for (int i = 0; i < typename.Length; ++i) {
514 char c = typename [i];
523 filename.Append ('`').Append ((numArgs+1).ToString());
538 return filename.ToString ();
541 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
543 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
544 index_assembly.SetAttribute ("Name", assembly.Name.Name);
545 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
547 AssemblyNameDefinition name = assembly.Name;
548 if (name.HasPublicKey) {
549 XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
550 var key = new StringBuilder (name.PublicKey.Length*3 + 2);
552 foreach (byte b in name.PublicKey)
553 key.AppendFormat ("{0,2:x2} ", b);
555 pubkey.InnerText = key.ToString ();
556 index_assembly.AppendChild (pubkey);
559 if (!string.IsNullOrEmpty (name.Culture)) {
560 XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
561 culture.InnerText = name.Culture;
562 index_assembly.AppendChild (culture);
565 MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
566 parent.AppendChild(index_assembly);
569 private void AddIndexType (TypeDefinition type, XmlElement index_types)
571 string typename = GetTypeFileName(type);
573 // Add namespace and type nodes into the index file as needed
574 string ns = DocUtils.GetNamespace (type);
575 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode ("Namespace[@Name='" + ns + "']");
576 if (nsnode == null) {
577 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
578 nsnode.SetAttribute ("Name", ns);
579 index_types.AppendChild (nsnode);
581 string doc_typename = GetDocTypeName (type);
582 XmlElement typenode = (XmlElement) nsnode.SelectSingleNode ("Type[@Name='" + typename + "']");
583 if (typenode == null) {
584 typenode = index_types.OwnerDocument.CreateElement ("Type");
585 typenode.SetAttribute ("Name", typename);
586 nsnode.AppendChild (typenode);
588 if (typename != doc_typename)
589 typenode.SetAttribute("DisplayName", doc_typename);
591 typenode.RemoveAttribute("DisplayName");
593 typenode.SetAttribute ("Kind", GetTypeKind (type));
596 private void DoUpdateAssemblies (string source, string dest)
598 string indexfile = dest + "/index.xml";
600 if (System.IO.File.Exists(indexfile)) {
601 index = new XmlDocument();
602 index.Load(indexfile);
605 ClearElement(index.DocumentElement, "Assembly");
606 ClearElement(index.DocumentElement, "Attributes");
608 index = CreateIndexStub();
611 string defaultTitle = "Untitled";
612 if (assemblies.Count == 1)
613 defaultTitle = assemblies[0].Name.Name;
614 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
616 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
617 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
618 index_assemblies.RemoveAll ();
621 HashSet<string> goodfiles = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
623 foreach (AssemblyDefinition assm in assemblies) {
624 AddIndexAssembly (assm, index_assemblies);
625 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
628 SortIndexEntries (index_types);
630 CleanupFiles (dest, goodfiles);
631 CleanupIndexTypes (index_types, goodfiles);
632 CleanupExtensions (index_types);
634 WriteFile (indexfile, FileMode.Create,
635 writer => WriteXml(index.DocumentElement, writer));
638 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
640 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
642 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
643 string typename = GetTypeFileName(type);
644 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0)
647 string reltypepath = DoUpdateType (type, source, dest);
648 if (reltypepath == null)
651 // Add namespace and type nodes into the index file as needed
652 AddIndexType (type, index_types);
654 // Ensure the namespace index file exists
655 string onsdoc = DocUtils.PathCombine (dest, type.Namespace + ".xml");
656 string nsdoc = DocUtils.PathCombine (dest, "ns-" + type.Namespace + ".xml");
657 if (File.Exists (onsdoc)) {
658 File.Move (onsdoc, nsdoc);
661 if (!File.Exists (nsdoc)) {
662 Console.WriteLine("New Namespace File: " + type.Namespace);
663 WriteNamespaceStub(type.Namespace, dest);
666 goodfiles.Add (reltypepath);
670 private static void SortIndexEntries (XmlElement indexTypes)
672 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
673 XmlNodeComparer c = new AttributeNameComparer ();
674 SortXmlNodes (indexTypes, namespaces, c);
676 for (int i = 0; i < namespaces.Count; ++i)
677 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
680 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
682 MyXmlNodeList l = new MyXmlNodeList (children.Count);
683 for (int i = 0; i < children.Count; ++i)
684 l.Add (children [i]);
686 for (int i = l.Count - 1; i > 0; --i) {
687 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
691 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
693 public abstract int Compare (XmlNode x, XmlNode y);
695 public int Compare (object x, object y)
697 return Compare ((XmlNode) x, (XmlNode) y);
701 class AttributeNameComparer : XmlNodeComparer {
704 public AttributeNameComparer ()
709 public AttributeNameComparer (string attribute)
711 this.attribute = attribute;
714 public override int Compare (XmlNode x, XmlNode y)
716 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
720 class VersionComparer : XmlNodeComparer {
721 public override int Compare (XmlNode x, XmlNode y)
723 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
724 string a = GetVersion (x.InnerText);
725 string b = GetVersion (y.InnerText);
726 return new Version (a).CompareTo (new Version (b));
729 static string GetVersion (string v)
731 int n = v.IndexOf ("x");
734 return v.Substring (0, n-1);
738 private static string GetTypeKind (TypeDefinition type)
741 return "Enumeration";
742 if (type.IsValueType)
744 if (type.IsInterface)
746 if (DocUtils.IsDelegate (type))
748 if (type.IsClass || type.FullName == "System.Enum") // FIXME
750 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
753 private static bool IsPublic (TypeDefinition type)
755 TypeDefinition decl = type;
756 while (decl != null) {
757 if (!(decl.IsPublic || decl.IsNestedPublic)) {
760 decl = (TypeDefinition) decl.DeclaringType;
765 private void CleanupFiles (string dest, HashSet<string> goodfiles)
767 // Look for files that no longer correspond to types
768 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
769 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
770 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
771 if (!goodfiles.Contains (relTypeFile)) {
772 XmlDocument doc = new XmlDocument ();
773 doc.Load (typefile.FullName);
774 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
775 if (!no_assembly_versions && UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
776 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
777 WriteXml(doc.DocumentElement, writer);
778 goodfiles.Add (relTypeFile);
781 string newname = typefile.FullName + ".remove";
782 try { System.IO.File.Delete(newname); } catch (Exception) { }
783 try { typefile.MoveTo(newname); } catch (Exception) { }
784 Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
790 private static TextWriter OpenWrite (string path, FileMode mode)
792 var w = new StreamWriter (
793 new FileStream (path, mode),
794 new UTF8Encoding (false)
800 private string[] GetAssemblyVersions ()
802 return (from a in assemblies select GetAssemblyVersion (a)).ToArray ();
805 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
807 // Look for type nodes that no longer correspond to types
808 MyXmlNodeList remove = new MyXmlNodeList ();
809 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
810 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
811 if (!goodfiles.Contains (fulltypename)) {
812 remove.Add (typenode);
815 foreach (XmlNode n in remove)
816 n.ParentNode.RemoveChild (n);
819 private void CleanupExtensions (XmlElement index_types)
821 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
822 if (extensionMethods.Count == 0) {
825 index_types.SelectSingleNode ("/Overview").RemoveChild (e);
829 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
830 index_types.SelectSingleNode ("/Overview").AppendChild (e);
834 extensionMethods.Sort (DefaultExtensionMethodComparer);
835 foreach (XmlNode m in extensionMethods) {
836 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
840 class ExtensionMethodComparer : XmlNodeComparer {
841 public override int Compare (XmlNode x, XmlNode y)
843 XmlNode xLink = x.SelectSingleNode ("Member/Link");
844 XmlNode yLink = y.SelectSingleNode ("Member/Link");
846 int n = xLink.Attributes ["Type"].Value.CompareTo (
847 yLink.Attributes ["Type"].Value);
850 n = xLink.Attributes ["Member"].Value.CompareTo (
851 yLink.Attributes ["Member"].Value);
852 if (n == 0 && !object.ReferenceEquals (x, y))
853 throw new InvalidOperationException ("Duplicate extension method found!");
858 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
860 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince)
862 Console.WriteLine(message + ": " + type.FullName);
864 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
866 // Update type metadata
867 UpdateType(basefile.DocumentElement, type);
869 // Update existing members. Delete member nodes that no longer should be there,
870 // and remember what members are already documented so we don't add them again.
872 MyXmlNodeList todelete = new MyXmlNodeList ();
873 foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
874 XmlElement oldmember = info.Node;
875 MemberReference oldmember2 = info.Member;
876 string sig = oldmember2 != null ? memberFormatters [0].GetDeclaration (oldmember2) : null;
878 // Interface implementations and overrides are deleted from the docs
879 // unless the overrides option is given.
880 if (oldmember2 != null && sig == null)
883 // Deleted (or signature changed)
884 if (oldmember2 == null) {
885 if (!no_assembly_versions && UpdateAssemblyVersions (oldmember, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
887 DeleteMember ("Member Removed", output, oldmember, todelete);
892 if (seenmembers.ContainsKey (sig)) {
893 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
894 // ignore, already seen
896 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
897 DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
899 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
903 // Update signature information
906 seenmembers.Add (sig, oldmember);
908 foreach (XmlElement oldmember in todelete)
909 oldmember.ParentNode.RemoveChild (oldmember);
912 if (!DocUtils.IsDelegate (type)) {
913 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
914 foreach (MemberReference m in type.GetMembers()) {
915 if (m is TypeDefinition) continue;
917 string sig = memberFormatters [0].GetDeclaration (m);
918 if (sig == null) continue;
919 if (seenmembers.ContainsKey(sig)) continue;
921 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
922 if (mm == null) continue;
923 members.AppendChild( mm );
925 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
930 // Import code snippets from files
931 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
932 if (!(code is XmlElement)) continue;
933 string file = ((XmlElement)code).GetAttribute("src");
934 string lang = ((XmlElement)code).GetAttribute("lang");
936 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
938 code.InnerText = src;
942 if (insertSince && since != null) {
943 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
944 docs.AppendChild (CreateSinceNode (basefile));
948 XmlElement d = basefile.DocumentElement ["Docs"];
949 XmlElement m = basefile.DocumentElement ["Members"];
950 if (d != null && m != null)
951 basefile.DocumentElement.InsertBefore (
952 basefile.DocumentElement.RemoveChild (d), m);
957 WriteXml(basefile.DocumentElement, Console.Out);
959 FileInfo file = new FileInfo (output);
960 if (!file.Directory.Exists) {
961 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
962 file.Directory.Create ();
964 WriteFile (output, FileMode.Create,
965 writer => WriteXml(basefile.DocumentElement, writer));
969 private string GetCodeSource (string lang, string file)
972 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
973 // Grab the specified region
974 string region = "#region " + file.Substring (anchorStart + 4);
975 file = file.Substring (0, anchorStart + 3);
977 using (StreamReader reader = new StreamReader (file)) {
979 StringBuilder src = new StringBuilder ();
981 while ((line = reader.ReadLine ()) != null) {
982 if (line.Trim() == region) {
983 indent = line.IndexOf (region);
986 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
991 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
994 return src.ToString ();
996 } catch (Exception e) {
997 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
998 file, region, show_exceptions ? e.ToString () : e.Message);
1003 using (StreamReader reader = new StreamReader (file))
1004 return reader.ReadToEnd ();
1005 } catch (Exception e) {
1006 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
1011 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
1013 string format = output != null
1014 ? "{0}: File='{1}'; Signature='{4}'"
1015 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1019 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1020 member.Attributes ["MemberName"].Value,
1021 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
1022 if (!delete && MemberDocsHaveUserContent (member)) {
1023 Warning ("Member deletions must be enabled with the --delete option.");
1025 todelete.Add (member);
1030 class MemberComparer : XmlNodeComparer {
1031 public override int Compare (XmlNode x, XmlNode y)
1034 string xMemberName = x.Attributes ["MemberName"].Value;
1035 string yMemberName = y.Attributes ["MemberName"].Value;
1037 // generic methods *end* with '>'
1038 // it's possible for explicitly implemented generic interfaces to
1039 // contain <...> without being a generic method
1040 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1041 (r = xMemberName.CompareTo (yMemberName)) != 0)
1045 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1046 xMemberName = xMemberName.Substring (0, lt);
1047 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1048 yMemberName = yMemberName.Substring (0, lt);
1049 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1052 // if @MemberName matches, then it's either two different types of
1053 // members sharing the same name, e.g. field & property, or it's an
1054 // overloaded method.
1055 // for different type, sort based on MemberType value.
1056 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1057 y.SelectSingleNode ("MemberType").InnerText);
1061 // same type -- must be an overloaded method. Sort based on type
1062 // parameter count, then parameter count, then by the parameter
1064 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1065 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1066 if (xTypeParams.Count != yTypeParams.Count)
1067 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1068 for (int i = 0; i < xTypeParams.Count; ++i) {
1069 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1070 yTypeParams [i].Attributes ["Name"].Value);
1075 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1076 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1077 if (xParams.Count != yParams.Count)
1078 return xParams.Count <= yParams.Count ? -1 : 1;
1079 for (int i = 0; i < xParams.Count; ++i) {
1080 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1081 yParams [i].Attributes ["Type"].Value);
1085 // all parameters match, but return value might not match if it was
1086 // changed between one version and another.
1087 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1088 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1089 if (xReturn != null && yReturn != null) {
1090 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1099 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1101 private static void SortTypeMembers (XmlNode members)
1103 if (members == null)
1105 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1108 private static bool MemberDocsHaveUserContent (XmlNode e)
1110 e = (XmlElement)e.SelectSingleNode("Docs");
1111 if (e == null) return false;
1112 foreach (XmlElement d in e.SelectNodes("*"))
1113 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1118 // UPDATE HELPER FUNCTIONS
1120 // CREATE A STUB DOCUMENTATION FILE
1122 public XmlElement StubType (TypeDefinition type, string output)
1124 string typesig = typeFormatters [0].GetDeclaration (type);
1125 if (typesig == null) return null; // not publicly visible
1127 XmlDocument doc = new XmlDocument();
1128 XmlElement root = doc.CreateElement("Type");
1129 doc.AppendChild (root);
1131 DoUpdateType2 ("New Type", doc, type, output, true);
1136 private XmlElement CreateSinceNode (XmlDocument doc)
1138 XmlElement s = doc.CreateElement ("since");
1139 s.SetAttribute ("version", since);
1143 // STUBBING/UPDATING FUNCTIONS
1145 public void UpdateType (XmlElement root, TypeDefinition type)
1147 root.SetAttribute("Name", GetDocTypeName (type));
1148 root.SetAttribute("FullName", GetDocTypeFullName (type));
1150 foreach (MemberFormatter f in typeFormatters) {
1151 string element = "TypeSignature[@Language='" + f.Language + "']";
1152 WriteElementAttribute (root, element, "Language", f.Language);
1153 WriteElementAttribute (root, element, "Value", f.GetDeclaration (type));
1156 XmlElement ass = WriteElement(root, "AssemblyInfo");
1157 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1158 if (!no_assembly_versions) {
1159 UpdateAssemblyVersions (root, type, true);
1162 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1163 foreach (var version in versions)
1164 ass.RemoveChild (version);
1166 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1167 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1169 ClearElement(ass, "AssemblyCulture");
1171 // Why-oh-why do we put assembly attributes in each type file?
1172 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1173 // since they're outdated in current docs, and a waste of space.
1174 //MakeAttributes(ass, type.Assembly, true);
1175 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1176 if (assattrs != null)
1177 ass.RemoveChild(assattrs);
1179 NormalizeWhitespace(ass);
1181 if (type.IsGenericType ()) {
1182 MakeTypeParameters (root, type.GenericParameters);
1184 ClearElement(root, "TypeParameters");
1187 if (type.BaseType != null) {
1188 XmlElement basenode = WriteElement(root, "Base");
1190 string basetypename = GetDocTypeFullName (type.BaseType);
1191 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1192 WriteElementText(root, "Base/BaseTypeName", basetypename);
1194 // Document how this type instantiates the generic parameters of its base type
1195 TypeReference origBase = type.BaseType.GetElementType ();
1196 if (origBase.IsGenericType ()) {
1197 ClearElement(basenode, "BaseTypeArguments");
1198 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1199 IList<TypeReference> baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1200 IList<GenericParameter> baseGenParams = origBase.GenericParameters;
1201 if (baseGenArgs.Count != baseGenParams.Count)
1202 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1203 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1204 GenericParameter param = baseGenParams [i];
1205 TypeReference value = baseGenArgs [i];
1207 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1208 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1209 bta.AppendChild(arg);
1210 arg.SetAttribute ("TypeParamName", param.Name);
1211 arg.InnerText = GetDocTypeFullName (value);
1215 ClearElement(root, "Base");
1218 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1219 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1220 List<string> interface_names = userInterfaces
1221 .Select (iface => GetDocTypeFullName (iface))
1225 XmlElement interfaces = WriteElement(root, "Interfaces");
1226 interfaces.RemoveAll();
1227 foreach (string iname in interface_names) {
1228 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1229 interfaces.AppendChild(iface);
1230 WriteElementText(iface, "InterfaceName", iname);
1233 ClearElement(root, "Interfaces");
1236 MakeAttributes (root, GetCustomAttributes (type));
1238 if (DocUtils.IsDelegate (type)) {
1239 MakeTypeParameters (root, type.GenericParameters);
1240 MakeParameters(root, type.GetMethod("Invoke").Parameters);
1241 MakeReturnValue(root, type.GetMethod("Invoke"));
1244 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1245 MakeDocNode (typeInfo);
1247 if (!DocUtils.IsDelegate (type))
1248 WriteElement (root, "Members");
1250 OrderTypeNodes (root, root.ChildNodes);
1251 NormalizeWhitespace(root);
1254 static readonly string[] TypeNodeOrder = {
1258 "ThreadingSafetyStatement",
1259 "ThreadSafetyStatement",
1271 static void OrderTypeNodes (XmlNode member, XmlNodeList children)
1273 ReorderNodes (member, children, TypeNodeOrder);
1276 internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1278 List<T> l = new List<T> (list);
1283 private void UpdateMember (DocsNodeInfo info)
1285 XmlElement me = (XmlElement) info.Node;
1286 MemberReference mi = info.Member;
1288 foreach (MemberFormatter f in memberFormatters) {
1289 string element = "MemberSignature[@Language='" + f.Language + "']";
1290 WriteElementAttribute (me, element, "Language", f.Language);
1291 WriteElementAttribute (me, element, "Value", f.GetDeclaration (mi));
1294 WriteElementText(me, "MemberType", GetMemberType(mi));
1296 if (!no_assembly_versions) {
1297 UpdateAssemblyVersions (me, mi, true);
1300 ClearElement (me, "AssemblyInfo");
1303 MakeAttributes (me, GetCustomAttributes (mi));
1305 MakeReturnValue(me, mi);
1306 if (mi is MethodReference) {
1307 MethodReference mb = (MethodReference) mi;
1308 if (mb.IsGenericMethod ())
1309 MakeTypeParameters (me, mb.GenericParameters);
1311 MakeParameters(me, mi);
1314 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1315 WriteElementText(me, "MemberValue", fieldValue);
1317 info.Node = WriteElement (me, "Docs");
1319 OrderMemberNodes (me, me.ChildNodes);
1320 UpdateExtensionMethods (me, info);
1323 static readonly string[] MemberNodeOrder = {
1338 static void OrderMemberNodes (XmlNode member, XmlNodeList children)
1340 ReorderNodes (member, children, MemberNodeOrder);
1343 static void ReorderNodes (XmlNode node, XmlNodeList children, string[] ordering)
1345 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1346 for (int i = 0; i < ordering.Length; ++i) {
1347 for (int j = 0; j < children.Count; ++j) {
1348 XmlNode c = children [j];
1349 if (c.Name == ordering [i]) {
1350 newChildren.Add (c);
1354 if (newChildren.Count >= 0)
1355 node.PrependChild ((XmlNode) newChildren [0]);
1356 for (int i = 1; i < newChildren.Count; ++i) {
1357 XmlNode prev = (XmlNode) newChildren [i-1];
1358 XmlNode cur = (XmlNode) newChildren [i];
1359 node.RemoveChild (cur);
1360 node.InsertAfter (cur, prev);
1364 IEnumerable<string> GetCustomAttributes (MemberReference mi)
1366 IEnumerable<string> attrs = Enumerable.Empty<string>();
1368 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1370 attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1372 PropertyDefinition pd = mi as PropertyDefinition;
1374 if (pd.GetMethod != null)
1375 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1376 if (pd.SetMethod != null)
1377 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1380 EventDefinition ed = mi as EventDefinition;
1382 if (ed.AddMethod != null)
1383 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1384 if (ed.RemoveMethod != null)
1385 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1391 IEnumerable<string> GetCustomAttributes (IList<CustomAttribute> attributes, string prefix)
1393 foreach (CustomAttribute attribute in attributes.OrderBy (ca => ca.AttributeType.FullName)) {
1395 TypeDefinition attrType = attribute.AttributeType as TypeDefinition;
1396 if (attrType != null && !IsPublic (attrType))
1398 if (slashdocFormatter.GetName (attribute.AttributeType) == null)
1401 if (Array.IndexOf (IgnorableAttributes, attribute.AttributeType.FullName) >= 0)
1404 StringList fields = new StringList ();
1406 for (int i = 0; i < attribute.ConstructorArguments.Count; ++i) {
1407 CustomAttributeArgument argument = attribute.ConstructorArguments [i];
1408 fields.Add (MakeAttributesValueString (
1413 (from namedArg in attribute.Fields
1414 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value })
1416 (from namedArg in attribute.Properties
1417 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value }))
1418 .OrderBy (v => v.Name);
1419 foreach (var d in namedArgs)
1420 fields.Add (string.Format ("{0}={1}", d.Name,
1421 MakeAttributesValueString (d.Value, d.Type)));
1423 string a2 = String.Join(", ", fields.ToArray ());
1424 if (a2 != "") a2 = "(" + a2 + ")";
1426 string name = attribute.GetDeclaringType();
1427 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1428 yield return prefix + name + a2;
1432 static readonly string[] ValidExtensionMembers = {
1441 static readonly string[] ValidExtensionDocMembers = {
1447 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1449 MethodDefinition me = info.Member as MethodDefinition;
1452 if (info.Parameters.Count < 1)
1454 if (!DocUtils.IsExtensionMethod (me))
1457 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1458 XmlNode member = e.CloneNode (true);
1459 em.AppendChild (member);
1460 RemoveExcept (member, ValidExtensionMembers);
1461 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1462 WriteElementText (member, "MemberType", "ExtensionMethod");
1463 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1464 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1465 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1466 member.AppendChild (link);
1467 AddTargets (em, info);
1469 extensionMethods.Add (em);
1472 private static void RemoveExcept (XmlNode node, string[] except)
1476 MyXmlNodeList remove = null;
1477 foreach (XmlNode n in node.ChildNodes) {
1478 if (Array.BinarySearch (except, n.Name) < 0) {
1480 remove = new MyXmlNodeList ();
1485 foreach (XmlNode n in remove)
1486 node.RemoveChild (n);
1489 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1491 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1492 member.PrependChild (targets);
1493 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1494 AppendElementAttributeText (targets, "Target", "Type",
1495 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1498 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1499 IList<TypeReference> constraints = gp.Constraints;
1500 if (constraints.Count == 0)
1501 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1503 foreach (TypeReference c in constraints)
1504 AppendElementAttributeText(targets, "Target", "Type",
1505 slashdocFormatter.GetDeclaration (c));
1509 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1512 TypeDefinition type = field.DeclaringType.Resolve ();
1513 if (type != null && type.IsEnum) return false;
1515 if (type != null && type.IsGenericType ()) return false;
1516 if (!field.HasConstant)
1518 if (field.IsLiteral) {
1519 object val = field.Constant;
1520 if (val == null) value = "null";
1521 else if (val is Enum) value = val.ToString();
1522 else if (val is IFormattable) {
1523 value = ((IFormattable)val).ToString();
1525 value = "\"" + value + "\"";
1527 if (value != null && value != "")
1533 // XML HELPER FUNCTIONS
1535 internal static XmlElement WriteElement(XmlNode parent, string element) {
1536 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1538 string[] path = element.Split('/');
1539 foreach (string p in path) {
1540 ret = (XmlElement)parent.SelectSingleNode(p);
1543 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1544 ename = ename.Substring(0, ename.IndexOf('['));
1545 ret = parent.OwnerDocument.CreateElement(ename);
1546 parent.AppendChild(ret);
1555 private static void WriteElementText(XmlNode parent, string element, string value) {
1556 XmlElement node = WriteElement(parent, element);
1557 node.InnerText = value;
1560 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1562 XmlElement n = parent.OwnerDocument.CreateElement (element);
1563 parent.AppendChild (n);
1564 n.InnerText = value;
1568 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1570 XmlElement n = parent.OwnerDocument.CreateElement (element);
1571 parent.AppendChild (n);
1572 n.SetAttribute (attribute, value);
1576 internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
1578 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1579 dest.AppendChild (copy);
1583 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1584 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1587 node = WriteElement(parent, element);
1588 node.InnerText = value;
1590 private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
1591 XmlElement node = WriteElement(parent, element);
1592 if (node.GetAttribute(attribute) == value) return;
1593 node.SetAttribute(attribute, value);
1595 internal static void ClearElement(XmlElement parent, string name) {
1596 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1598 parent.RemoveChild(node);
1601 // DOCUMENTATION HELPER FUNCTIONS
1603 private void MakeDocNode (DocsNodeInfo info)
1605 List<GenericParameter> genericParams = info.GenericParameters;
1606 IList<ParameterDefinition> parameters = info.Parameters;
1607 TypeReference returntype = info.ReturnType;
1608 bool returnisreturn = info.ReturnIsReturn;
1609 XmlElement e = info.Node;
1610 bool addremarks = info.AddRemarks;
1612 WriteElementInitialText(e, "summary", "To be added.");
1614 if (parameters != null) {
1615 string[] values = new string [parameters.Count];
1616 for (int i = 0; i < values.Length; ++i)
1617 values [i] = parameters [i].Name;
1618 UpdateParameters (e, "param", values);
1621 if (genericParams != null) {
1622 string[] values = new string [genericParams.Count];
1623 for (int i = 0; i < values.Length; ++i)
1624 values [i] = genericParams [i].Name;
1625 UpdateParameters (e, "typeparam", values);
1628 string retnodename = null;
1629 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
1630 retnodename = returnisreturn ? "returns" : "value";
1631 string retnodename_other = !returnisreturn ? "returns" : "value";
1633 // If it has a returns node instead of a value node, change its name.
1634 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1635 if (retother != null) {
1636 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1637 foreach (XmlNode node in retother)
1638 retnode.AppendChild(node.CloneNode(true));
1639 e.ReplaceChild(retnode, retother);
1641 WriteElementInitialText(e, retnodename, "To be added.");
1644 ClearElement(e, "returns");
1645 ClearElement(e, "value");
1649 WriteElementInitialText(e, "remarks", "To be added.");
1651 if (exceptions.HasValue && info.Member != null &&
1652 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
1653 UpdateExceptions (e, info.Member);
1656 foreach (DocumentationImporter importer in importers)
1657 importer.ImportDocumentation (info);
1659 OrderDocsNodes (e, e.ChildNodes);
1660 NormalizeWhitespace(e);
1663 static readonly string[] DocsNodeOrder = {
1664 "typeparam", "param", "summary", "returns", "value", "remarks",
1667 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
1669 ReorderNodes (docs, children, DocsNodeOrder);
1673 private void UpdateParameters (XmlElement e, string element, string[] values)
1675 if (values != null) {
1676 XmlNode[] paramnodes = new XmlNode[values.Length];
1678 // Some documentation had param nodes with leading spaces.
1679 foreach (XmlElement paramnode in e.SelectNodes(element)){
1680 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
1683 // If a member has only one parameter, we can track changes to
1684 // the name of the parameter easily.
1685 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
1686 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
1689 bool reinsert = false;
1691 // Pick out existing and still-valid param nodes, and
1692 // create nodes for parameters not in the file.
1693 Hashtable seenParams = new Hashtable();
1694 for (int pi = 0; pi < values.Length; pi++) {
1695 string p = values [pi];
1698 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
1699 if (paramnodes[pi] != null) continue;
1701 XmlElement pe = e.OwnerDocument.CreateElement(element);
1702 pe.SetAttribute("name", p);
1703 pe.InnerText = "To be added.";
1704 paramnodes[pi] = pe;
1708 // Remove parameters that no longer exist and check all params are in the right order.
1710 MyXmlNodeList todelete = new MyXmlNodeList ();
1711 foreach (XmlElement paramnode in e.SelectNodes(element)) {
1712 string name = paramnode.GetAttribute("name");
1713 if (!seenParams.ContainsKey(name)) {
1714 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1715 Warning ("The following param node can only be deleted if the --delete option is given: ");
1716 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
1718 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
1719 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1723 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
1724 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1725 e.ParentNode.Attributes ["MemberName"].Value,
1728 Warning ("\tValue={0}", paramnode.OuterXml);
1730 todelete.Add (paramnode);
1735 if ((int)seenParams[name] != idx)
1741 foreach (XmlNode n in todelete) {
1742 n.ParentNode.RemoveChild (n);
1745 // Re-insert the parameter nodes at the top of the doc section.
1747 for (int pi = values.Length-1; pi >= 0; pi--)
1748 e.PrependChild(paramnodes[pi]);
1750 // Clear all existing param nodes
1751 foreach (XmlNode paramnode in e.SelectNodes(element)) {
1752 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1753 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
1754 Console.WriteLine(paramnode.OuterXml);
1756 paramnode.ParentNode.RemoveChild(paramnode);
1762 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
1764 string existingName = pe.GetAttribute ("name");
1765 pe.SetAttribute ("name", newName);
1766 if (existingName == newName)
1768 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
1769 if (paramref.GetAttribute ("name").Trim () == existingName)
1770 paramref.SetAttribute ("name", newName);
1773 class CrefComparer : XmlNodeComparer {
1775 public CrefComparer ()
1779 public override int Compare (XmlNode x, XmlNode y)
1781 string xType = x.Attributes ["cref"].Value;
1782 string yType = y.Attributes ["cref"].Value;
1783 string xNamespace = GetNamespace (xType);
1784 string yNamespace = GetNamespace (yType);
1786 int c = xNamespace.CompareTo (yNamespace);
1789 return xType.CompareTo (yType);
1792 static string GetNamespace (string type)
1794 int n = type.LastIndexOf ('.');
1796 return type.Substring (0, n);
1797 return string.Empty;
1801 private void UpdateExceptions (XmlNode docs, MemberReference member)
1803 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
1804 string cref = slashdocFormatter.GetDeclaration (source.Exception);
1805 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
1808 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
1809 e.SetAttribute ("cref", cref);
1810 e.InnerXml = "To be added; from: <see cref=\"" +
1811 string.Join ("\" />, <see cref=\"",
1812 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
1815 docs.AppendChild (e);
1817 SortXmlNodes (docs, docs.SelectNodes ("exception"),
1818 new CrefComparer ());
1821 private static void NormalizeWhitespace(XmlElement e) {
1822 // Remove all text and whitespace nodes from the element so it
1823 // is outputted with nice indentation and no blank lines.
1824 ArrayList deleteNodes = new ArrayList();
1825 foreach (XmlNode n in e)
1826 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
1828 foreach (XmlNode n in deleteNodes)
1829 n.ParentNode.RemoveChild(n);
1832 private static bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
1834 TypeDefinition type = member as TypeDefinition;
1836 type = member.DeclaringType as TypeDefinition;
1837 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
1840 private static string GetAssemblyVersion (AssemblyDefinition assembly)
1842 return assembly.Name.Version.ToString();
1845 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
1847 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
1849 e = root.OwnerDocument.CreateElement("AssemblyInfo");
1850 root.AppendChild(e);
1852 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
1853 .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
1855 // matches.Count > 0 && add: ignore -- already present
1856 if (matches.Count > 0 && !add) {
1857 foreach (XmlNode c in matches)
1860 else if (matches.Count == 0 && add) {
1861 foreach (string sv in assemblyVersions) {
1862 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
1867 // matches.Count == 0 && !add: ignore -- already not present
1869 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
1870 SortXmlNodes (e, avs, new VersionComparer ());
1872 return avs.Count != 0;
1875 // FIXME: get TypeReferences instead of string comparison?
1876 private static string[] IgnorableAttributes = {
1877 // Security related attributes
1878 "System.Reflection.AssemblyKeyFileAttribute",
1879 "System.Reflection.AssemblyDelaySignAttribute",
1880 // Present in @RefType
1881 "System.Runtime.InteropServices.OutAttribute",
1882 // For naming the indexer to use when not using indexers
1883 "System.Reflection.DefaultMemberAttribute",
1884 // for decimal constants
1885 "System.Runtime.CompilerServices.DecimalConstantAttribute",
1886 // compiler generated code
1887 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
1888 // more compiler generated code, e.g. iterator methods
1889 "System.Diagnostics.DebuggerHiddenAttribute",
1890 "System.Runtime.CompilerServices.FixedBufferAttribute",
1891 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
1892 // extension methods
1893 "System.Runtime.CompilerServices.ExtensionAttribute",
1894 // Used to differentiate 'object' from C#4 'dynamic'
1895 "System.Runtime.CompilerServices.DynamicAttribute",
1898 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes)
1900 if (!attributes.Any ()) {
1901 ClearElement (root, "Attributes");
1905 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
1909 e = root.OwnerDocument.CreateElement("Attributes");
1911 foreach (string attribute in attributes) {
1912 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
1915 WriteElementText(ae, "AttributeName", attribute);
1918 if (e.ParentNode == null)
1919 root.AppendChild(e);
1921 NormalizeWhitespace(e);
1924 public static string MakeAttributesValueString (object v, TypeReference valueType)
1928 if (valueType.FullName == "System.Type")
1929 return "typeof(" + v.ToString () + ")";
1930 if (valueType.FullName == "System.String")
1931 return "\"" + v.ToString () + "\"";
1932 if (valueType.FullName == "System.Char")
1933 return "'" + v.ToString () + "'";
1935 return (bool)v ? "true" : "false";
1936 TypeDefinition valueDef = valueType.Resolve ();
1937 if (valueDef == null || !valueDef.IsEnum)
1938 return v.ToString ();
1939 string typename = GetDocTypeFullName (valueType);
1940 var values = GetEnumerationValues (valueDef);
1941 long c = ToInt64 (v);
1942 if (values.ContainsKey (c))
1943 return typename + "." + values [c];
1944 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
1945 return string.Join (" | ",
1946 (from i in values.Keys
1948 select typename + "." + values [i])
1951 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
1954 private static Dictionary<long, string> GetEnumerationValues (TypeDefinition type)
1956 var values = new Dictionary<long, string> ();
1958 (from f in type.Fields
1959 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
1961 values [ToInt64 (f.Constant)] = f.Name;
1966 static long ToInt64 (object value)
1969 return (long) (ulong) value;
1970 return Convert.ToInt64 (value);
1973 private void MakeParameters (XmlElement root, IList<ParameterDefinition> parameters)
1975 XmlElement e = WriteElement(root, "Parameters");
1977 foreach (ParameterDefinition p in parameters) {
1978 XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
1980 pe.SetAttribute("Name", p.Name);
1981 pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
1982 if (p.ParameterType is ByReferenceType) {
1983 if (p.IsOut) pe.SetAttribute("RefType", "out");
1984 else pe.SetAttribute("RefType", "ref");
1986 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
1990 private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams)
1992 if (typeParams == null || typeParams.Count == 0) {
1993 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
1995 root.RemoveChild (f);
1998 XmlElement e = WriteElement(root, "TypeParameters");
2000 foreach (GenericParameter t in typeParams) {
2001 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2003 pe.SetAttribute("Name", t.Name);
2004 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""));
2005 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2006 IList<TypeReference> constraints = t.Constraints;
2007 GenericParameterAttributes attrs = t.Attributes;
2008 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2016 ce = root.OwnerDocument.CreateElement ("Constraints");
2018 pe.AppendChild (ce);
2019 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2020 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2021 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2022 AppendElementText (ce, "ParameterAttribute", "Covariant");
2023 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2024 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2025 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2026 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2027 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2028 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2029 foreach (TypeReference c in constraints) {
2030 TypeDefinition cd = c.Resolve ();
2031 AppendElementText (ce,
2032 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2033 GetDocTypeFullName (c));
2038 private void MakeParameters (XmlElement root, MemberReference mi)
2040 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2041 MakeParameters (root, ((MethodDefinition)mi).Parameters);
2042 else if (mi is MethodDefinition) {
2043 MethodDefinition mb = (MethodDefinition) mi;
2044 IList<ParameterDefinition> parameters = mb.Parameters;
2045 MakeParameters(root, parameters);
2046 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2047 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2048 p.SetAttribute ("RefType", "this");
2051 else if (mi is PropertyDefinition) {
2052 IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
2053 if (parameters.Count > 0)
2054 MakeParameters(root, parameters);
2058 else if (mi is FieldDefinition) return;
2059 else if (mi is EventDefinition) return;
2060 else throw new ArgumentException();
2063 internal static string GetDocParameterType (TypeReference type)
2065 return GetDocTypeFullName (type).Replace ("@", "&");
2068 private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes)
2070 XmlElement e = WriteElement(root, "ReturnValue");
2072 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2073 if (attributes != null)
2074 MakeAttributes(e, GetCustomAttributes (attributes, ""));
2077 private void MakeReturnValue (XmlElement root, MemberReference mi)
2079 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2081 else if (mi is MethodDefinition)
2082 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes);
2083 else if (mi is PropertyDefinition)
2084 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
2085 else if (mi is FieldDefinition)
2086 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
2087 else if (mi is EventDefinition)
2088 MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
2090 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2093 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2095 MemberReference mi = info.Member;
2096 if (mi is TypeDefinition) return null;
2098 string sigs = memberFormatters [0].GetDeclaration (mi);
2099 if (sigs == null) return null; // not publicly visible
2101 // no documentation for property/event accessors. Is there a better way of doing this?
2102 if (mi.Name.StartsWith("get_")) return null;
2103 if (mi.Name.StartsWith("set_")) return null;
2104 if (mi.Name.StartsWith("add_")) return null;
2105 if (mi.Name.StartsWith("remove_")) return null;
2106 if (mi.Name.StartsWith("raise_")) return null;
2108 XmlElement me = doc.CreateElement("Member");
2109 me.SetAttribute("MemberName", GetMemberName (mi));
2113 if (exceptions.HasValue &&
2114 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2115 UpdateExceptions (info.Node, info.Member);
2117 if (since != null) {
2118 XmlNode docs = me.SelectSingleNode("Docs");
2119 docs.AppendChild (CreateSinceNode (doc));
2125 internal static string GetMemberName (MemberReference mi)
2127 MethodDefinition mb = mi as MethodDefinition;
2129 PropertyDefinition pi = mi as PropertyDefinition;
2132 return DocUtils.GetPropertyName (pi);
2134 StringBuilder sb = new StringBuilder (mi.Name.Length);
2135 if (!DocUtils.IsExplicitlyImplemented (mb))
2136 sb.Append (mi.Name);
2138 TypeReference iface;
2139 MethodReference ifaceMethod;
2140 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2141 sb.Append (GetDocTypeFullName (iface));
2143 sb.Append (ifaceMethod.Name);
2145 if (mb.IsGenericMethod ()) {
2146 IList<GenericParameter> typeParams = mb.GenericParameters;
2147 if (typeParams.Count > 0) {
2149 sb.Append (typeParams [0].Name);
2150 for (int i = 1; i < typeParams.Count; ++i)
2151 sb.Append (",").Append (typeParams [i].Name);
2155 return sb.ToString ();
2158 /// SIGNATURE GENERATION FUNCTIONS
2159 internal static bool IsPrivate (MemberReference mi)
2161 return memberFormatters [0].GetDeclaration (mi) == null;
2164 internal static string GetMemberType (MemberReference mi)
2166 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2167 return "Constructor";
2168 if (mi is MethodDefinition)
2170 if (mi is PropertyDefinition)
2172 if (mi is FieldDefinition)
2174 if (mi is EventDefinition)
2176 throw new ArgumentException();
2179 private static string GetDocTypeName (TypeReference type)
2181 return docTypeFormatter.GetName (type);
2184 internal static string GetDocTypeFullName (TypeReference type)
2186 return DocTypeFullMemberFormatter.Default.GetName (type);
2189 internal static string GetXPathForMember (DocumentationMember member)
2191 StringBuilder xpath = new StringBuilder ();
2192 xpath.Append ("//Members/Member[@MemberName=\"")
2193 .Append (member.MemberName)
2195 if (member.Parameters != null && member.Parameters.Count > 0) {
2196 xpath.Append ("/Parameters[count(Parameter) = ")
2197 .Append (member.Parameters.Count);
2198 for (int i = 0; i < member.Parameters.Count; ++i) {
2199 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2200 xpath.Append (member.Parameters [i]);
2201 xpath.Append ("\"");
2203 xpath.Append ("]/..");
2205 return xpath.ToString ();
2208 public static string GetXPathForMember (XPathNavigator member)
2210 StringBuilder xpath = new StringBuilder ();
2211 xpath.Append ("//Type[@FullName=\"")
2212 .Append (member.SelectSingleNode ("../../@FullName").Value)
2214 xpath.Append ("Members/Member[@MemberName=\"")
2215 .Append (member.SelectSingleNode ("@MemberName").Value)
2217 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2218 if (parameters.Count > 0) {
2219 xpath.Append ("/Parameters[count(Parameter) = ")
2220 .Append (parameters.Count);
2222 while (parameters.MoveNext ()) {
2224 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2225 xpath.Append (parameters.Current.Value);
2226 xpath.Append ("\"");
2228 xpath.Append ("]/..");
2230 return xpath.ToString ();
2233 public static string GetXPathForMember (MemberReference member)
2235 StringBuilder xpath = new StringBuilder ();
2236 xpath.Append ("//Type[@FullName=\"")
2237 .Append (member.DeclaringType.FullName)
2239 xpath.Append ("Members/Member[@MemberName=\"")
2240 .Append (GetMemberName (member))
2243 IList<ParameterDefinition> parameters = null;
2244 if (member is MethodDefinition)
2245 parameters = ((MethodDefinition) member).Parameters;
2246 else if (member is PropertyDefinition) {
2247 parameters = ((PropertyDefinition) member).Parameters;
2249 if (parameters != null && parameters.Count > 0) {
2250 xpath.Append ("/Parameters[count(Parameter) = ")
2251 .Append (parameters.Count);
2252 for (int i = 0; i < parameters.Count; ++i) {
2253 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2254 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2255 xpath.Append ("\"");
2257 xpath.Append ("]/..");
2259 return xpath.ToString ();
2263 static class CecilExtensions {
2264 public static string GetDeclaringType(this CustomAttribute attribute)
2266 return attribute.Constructor.DeclaringType.FullName;
2269 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
2271 foreach (var c in type.Methods.Where (m => m.IsConstructor))
2272 yield return (MemberReference) c;
2273 foreach (var e in type.Events)
2274 yield return (MemberReference) e;
2275 foreach (var f in type.Fields)
2276 yield return (MemberReference) f;
2277 foreach (var m in type.Methods.Where (m => !m.IsConstructor))
2278 yield return (MemberReference) m;
2279 foreach (var t in type.NestedTypes)
2280 yield return (MemberReference) t;
2281 foreach (var p in type.Properties)
2282 yield return (MemberReference) p;
2285 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
2287 return GetMembers (type).Where (m => m.Name == member);
2290 public static MemberReference GetMember (this TypeDefinition type, string member)
2292 return GetMembers (type, member).EnsureZeroOrOne ();
2295 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2297 if (source.Count () > 1)
2298 throw new InvalidOperationException ("too many matches");
2299 return source.FirstOrDefault ();
2302 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2305 .Where (m => m.Name == method)
2306 .EnsureZeroOrOne ();
2309 public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
2311 TypeDefinition def = type as TypeDefinition;
2313 return new MemberReference [0];
2314 CustomAttribute defMemberAttr = def.CustomAttributes
2315 .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
2316 if (defMemberAttr == null)
2317 return new MemberReference [0];
2318 string name = (string) defMemberAttr.ConstructorArguments [0].Value;
2319 return def.Properties
2320 .Where (p => p.Name == name)
2321 .Select (p => (MemberReference) p);
2324 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2326 return assembly.Modules.SelectMany (md => md.GetAllTypes ());
2329 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2331 return GetTypes (assembly)
2332 .Where (td => td.FullName == type)
2333 .EnsureZeroOrOne ();
2336 public static bool IsGenericType (this TypeReference type)
2338 return type.GenericParameters.Count > 0;
2341 public static bool IsGenericMethod (this MethodReference method)
2343 return method.GenericParameters.Count > 0;
2346 public static MemberReference Resolve (this MemberReference member)
2348 FieldReference fr = member as FieldReference;
2350 return fr.Resolve ();
2351 MethodReference mr = member as MethodReference;
2353 return mr.Resolve ();
2354 TypeReference tr = member as TypeReference;
2356 return tr.Resolve ();
2357 PropertyReference pr = member as PropertyReference;
2360 EventReference er = member as EventReference;
2363 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2366 public static TypeReference GetUnderlyingType (this TypeDefinition type)
2370 return type.Fields.First (f => f.Name == "value__").FieldType;
2373 public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
2375 return self.Types.SelectMany (t => t.GetAllTypes ());
2378 static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
2382 if (!self.HasNestedTypes)
2385 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
2390 static class DocUtils {
2391 public static bool IsExplicitlyImplemented (MethodDefinition method)
2393 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2396 public static string GetTypeDotMember (string name)
2398 int startType, startMethod;
2399 startType = startMethod = -1;
2400 for (int i = 0; i < name.Length; ++i) {
2401 if (name [i] == '.') {
2402 startType = startMethod;
2406 return name.Substring (startType+1);
2409 public static string GetMember (string name)
2411 int i = name.LastIndexOf ('.');
2414 return name.Substring (i+1);
2417 public static void GetInfoForExplicitlyImplementedMethod (
2418 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2422 if (method.Overrides.Count != 1)
2423 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2424 iface = method.Overrides [0].DeclaringType;
2425 ifaceMethod = method.Overrides [0];
2428 public static string GetPropertyName (PropertyDefinition pi)
2430 // Issue: (g)mcs-generated assemblies that explicitly implement
2431 // properties don't specify the full namespace, just the
2432 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2433 MethodDefinition method = pi.GetMethod;
2435 method = pi.SetMethod;
2436 if (!IsExplicitlyImplemented (method))
2439 // Need to determine appropriate namespace for this member.
2440 TypeReference iface;
2441 MethodReference ifaceMethod;
2442 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2443 return string.Join (".", new string[]{
2444 DocTypeFullMemberFormatter.Default.GetName (iface),
2445 GetMember (pi.Name)});
2448 public static string GetNamespace (TypeReference type)
2450 if (type.GetElementType ().IsNested)
2451 type = type.GetElementType ();
2452 while (type != null && type.IsNested)
2453 type = type.DeclaringType;
2455 return string.Empty;
2456 return type.Namespace;
2459 public static string PathCombine (string dir, string path)
2465 return Path.Combine (dir, path);
2468 public static bool IsExtensionMethod (MethodDefinition method)
2471 method.CustomAttributes
2472 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2473 && method.DeclaringType.CustomAttributes
2474 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
2477 public static bool IsDelegate (TypeDefinition type)
2479 TypeReference baseRef = type.BaseType;
2480 if (baseRef == null)
2482 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2483 baseRef.FullName == "System.MulticastDelegate";
2486 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2488 List<TypeReference> decls = new List<TypeReference> ();
2490 while (type.DeclaringType != null) {
2491 decls.Add (type.DeclaringType);
2492 type = type.DeclaringType;
2498 public static int GetGenericArgumentCount (TypeReference type)
2500 GenericInstanceType inst = type as GenericInstanceType;
2502 ? inst.GenericArguments.Count
2503 : type.GenericParameters.Count;
2506 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
2508 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
2509 List<TypeReference> userInterfaces = new List<TypeReference> ();
2510 foreach (TypeReference iface in type.Interfaces) {
2511 TypeReference lookup = iface.Resolve () ?? iface;
2512 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
2513 userInterfaces.Add (iface);
2515 return userInterfaces;
2518 private static string GetQualifiedTypeName (TypeReference type)
2520 return "[" + type.Scope.Name + "]" + type.FullName;
2523 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
2525 HashSet<string> inheritedInterfaces = new HashSet<string> ();
2526 Action<TypeDefinition> a = null;
2528 if (t == null) return;
2529 foreach (TypeReference r in t.Interfaces) {
2530 inheritedInterfaces.Add (GetQualifiedTypeName (r));
2534 TypeReference baseRef = type.BaseType;
2535 while (baseRef != null) {
2536 TypeDefinition baseDef = baseRef.Resolve ();
2537 if (baseDef != null) {
2539 baseRef = baseDef.BaseType;
2544 foreach (TypeReference r in type.Interfaces)
2546 return inheritedInterfaces;
2550 class DocsNodeInfo {
2551 public DocsNodeInfo (XmlElement node)
2556 public DocsNodeInfo (XmlElement node, TypeDefinition type)
2562 public DocsNodeInfo (XmlElement node, MemberReference member)
2565 SetMemberInfo (member);
2568 void SetType (TypeDefinition type)
2571 throw new ArgumentNullException ("type");
2573 GenericParameters = new List<GenericParameter> (type.GenericParameters);
2574 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
2575 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
2576 for (int i = 0; i < declTypes.Count - 1; ++i) {
2577 int remove = System.Math.Min (maxGenArgs,
2578 DocUtils.GetGenericArgumentCount (declTypes [i]));
2579 maxGenArgs -= remove;
2580 while (remove-- > 0)
2581 GenericParameters.RemoveAt (0);
2583 if (DocUtils.IsDelegate (type)) {
2584 Parameters = type.GetMethod("Invoke").Parameters;
2585 ReturnType = type.GetMethod("Invoke").ReturnType;
2586 ReturnIsReturn = true;
2590 void SetMemberInfo (MemberReference member)
2593 throw new ArgumentNullException ("member");
2594 ReturnIsReturn = true;
2598 if (member is MethodReference ) {
2599 MethodReference mr = (MethodReference) member;
2600 Parameters = mr.Parameters;
2601 if (mr.IsGenericMethod ()) {
2602 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
2605 else if (member is PropertyDefinition) {
2606 Parameters = ((PropertyDefinition) member).Parameters;
2609 if (member is MethodDefinition) {
2610 ReturnType = ((MethodDefinition) member).ReturnType;
2611 } else if (member is PropertyDefinition) {
2612 ReturnType = ((PropertyDefinition) member).PropertyType;
2613 ReturnIsReturn = false;
2616 // no remarks section for enum members
2617 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
2621 public TypeReference ReturnType;
2622 public List<GenericParameter> GenericParameters;
2623 public IList<ParameterDefinition> Parameters;
2624 public bool ReturnIsReturn;
2625 public XmlElement Node;
2626 public bool AddRemarks = true;
2627 public MemberReference Member;
2628 public TypeDefinition Type;
2631 class DocumentationEnumerator {
2633 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2635 return GetDocumentationTypes (assembly, forTypes, null);
2638 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2640 foreach (TypeDefinition type in assembly.GetTypes()) {
2641 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
2643 if (seen != null && seen.Contains (type.FullName))
2646 foreach (TypeDefinition nested in type.NestedTypes)
2647 yield return nested;
2651 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2653 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
2654 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
2655 oldmember.RemoveAttribute ("__monodocer-seen__");
2658 MemberReference m = GetMember (type, new DocumentationMember (oldmember));
2660 yield return new DocsNodeInfo (oldmember);
2663 yield return new DocsNodeInfo (oldmember, m);
2668 protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
2670 string membertype = member.MemberType;
2672 string returntype = member.ReturnType;
2674 string docName = member.MemberName;
2675 string[] docTypeParams = GetTypeParameters (docName);
2677 // Loop through all members in this type with the same name
2678 foreach (MemberReference mi in GetReflectionMembers (type, docName)) {
2679 if (mi is TypeDefinition) continue;
2680 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
2682 if (MDocUpdater.IsPrivate (mi))
2685 IList<ParameterDefinition> pis = null;
2686 string[] typeParams = null;
2687 if (mi is MethodDefinition) {
2688 MethodDefinition mb = (MethodDefinition) mi;
2689 pis = mb.Parameters;
2690 if (docTypeParams != null && mb.IsGenericMethod ()) {
2691 IList<GenericParameter> args = mb.GenericParameters;
2692 if (args.Count == docTypeParams.Length) {
2693 typeParams = args.Select (p => p.Name).ToArray ();
2697 else if (mi is PropertyDefinition)
2698 pis = ((PropertyDefinition)mi).Parameters;
2700 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
2701 int pcount = pis == null ? 0 : pis.Count;
2702 if (mcount != pcount)
2705 MethodDefinition mDef = mi as MethodDefinition;
2706 if (mDef != null && !mDef.IsConstructor) {
2707 // Casting operators can overload based on return type.
2708 if (returntype != GetReplacedString (
2709 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
2710 typeParams, docTypeParams)) {
2718 for (int i = 0; i < pis.Count; i++) {
2719 string paramType = GetReplacedString (
2720 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
2721 typeParams, docTypeParams);
2722 if (paramType != (string) member.Parameters [i]) {
2727 if (!good) continue;
2735 static string[] GetTypeParameters (string docName)
2737 if (docName [docName.Length-1] != '>')
2739 StringList types = new StringList ();
2740 int endToken = docName.Length-2;
2741 int i = docName.Length-2;
2743 if (docName [i] == ',' || docName [i] == '<') {
2744 types.Add (docName.Substring (i + 1, endToken - i));
2747 if (docName [i] == '<')
2752 return types.ToArray ();
2755 protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
2757 // need to worry about 4 forms of //@MemberName values:
2758 // 1. "Normal" (non-generic) member names: GetEnumerator
2760 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
2761 // - try as-is, and try type.member (due to "kludge" for property
2763 // 3. "Normal" Generic member names: Sort<T> (CSC)
2764 // - need to remove generic parameters --> "Sort"
2765 // 4. Explicitly-implemented interface members for generic interfaces:
2766 // -- System.Collections.Generic.IEnumerable<T>.Current
2767 // - Try as-is, and try type.member, *keeping* the generic parameters.
2768 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
2769 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
2770 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
2771 // this as (1) or (2).
2772 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
2774 foreach (MemberReference mi in type.GetMembers (docName))
2776 if (CountChars (docName, '.') > 0)
2777 // might be a property; try only type.member instead of
2778 // namespace.type.member.
2779 foreach (MemberReference mi in
2780 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
2787 int startLt, startType, startMethod;
2788 startLt = startType = startMethod = -1;
2789 for (int i = 0; i < docName.Length; ++i) {
2790 switch (docName [i]) {
2799 if (numLt == 0 && (i + 1) < docName.Length)
2800 // there's another character in docName, so this <...> sequence is
2801 // probably part of a generic type -- case 4.
2805 startType = startMethod;
2811 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
2813 foreach (MemberReference mi in type.GetMembers (refName))
2817 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
2820 // If we _still_ haven't found it, we've hit another generic naming issue:
2821 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
2822 // explicitly-implemented METHOD names (not properties), e.g.
2823 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
2824 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
2825 // which the XML docs will contain.
2827 // Alas, we can't derive the Mono name from docName, so we need to iterate
2828 // over all member names, convert them into CSC format, and compare... :-(
2831 foreach (MemberReference mi in type.GetMembers ()) {
2832 if (MDocUpdater.GetMemberName (mi) == docName)
2837 static string GetReplacedString (string typeName, string[] from, string[] to)
2841 for (int i = 0; i < from.Length; ++i)
2842 typeName = typeName.Replace (from [i], to [i]);
2846 private static int CountChars (string s, char c)
2849 for (int i = 0; i < s.Length; ++i) {
2857 class EcmaDocumentationEnumerator : DocumentationEnumerator {
2862 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
2865 this.ecmadocs = ecmaDocs;
2868 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2870 HashSet<string> seen = new HashSet<string> ();
2871 return GetDocumentationTypes (assembly, forTypes, seen)
2872 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
2875 new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2878 while (ecmadocs.Read ()) {
2879 switch (ecmadocs.Name) {
2881 if (typeDepth == -1)
2882 typeDepth = ecmadocs.Depth;
2883 if (ecmadocs.NodeType != XmlNodeType.Element)
2885 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
2887 string typename = ecmadocs.GetAttribute ("FullName");
2888 string typename2 = MDocUpdater.GetTypeFileName (typename);
2889 if (forTypes != null &&
2890 forTypes.BinarySearch (typename) < 0 &&
2891 typename != typename2 &&
2892 forTypes.BinarySearch (typename2) < 0)
2895 if ((t = assembly.GetType (typename)) == null &&
2896 (t = assembly.GetType (typename2)) == null)
2898 seen.Add (typename);
2899 if (typename != typename2)
2900 seen.Add (typename2);
2901 Console.WriteLine (" Import: {0}", t.FullName);
2902 if (ecmadocs.Name != "Docs") {
2903 int depth = ecmadocs.Depth;
2904 while (ecmadocs.Read ()) {
2905 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
2909 if (!ecmadocs.IsStartElement ("Docs"))
2910 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
2920 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2922 return GetMembers (basefile, type)
2923 .Concat (base.GetDocumentationMembers (basefile, type));
2926 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
2928 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
2931 if (ecmadocs.IsEmptyElement)
2934 int membersDepth = ecmadocs.Depth;
2936 while (go && ecmadocs.Read ()) {
2937 switch (ecmadocs.Name) {
2939 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
2941 DocumentationMember dm = new DocumentationMember (ecmadocs);
2942 string xp = MDocUpdater.GetXPathForMember (dm);
2943 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
2945 if (oldmember == null) {
2946 m = GetMember (type, dm);
2948 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2949 type.FullName, dm.MemberSignatures ["C#"]);
2950 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
2953 // oldmember lookup may have failed due to type parameter renames.
2955 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
2956 if (oldmember == null) {
2957 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
2958 oldmember = basefile.CreateElement ("Member");
2959 oldmember.SetAttribute ("MemberName", dm.MemberName);
2960 members.AppendChild (oldmember);
2961 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
2962 XmlElement ms = basefile.CreateElement ("MemberSignature");
2963 ms.SetAttribute ("Language", key);
2964 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
2965 oldmember.AppendChild (ms);
2967 oldmember.SetAttribute ("__monodocer-seen__", "true");
2968 Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
2973 m = GetMember (type, new DocumentationMember (oldmember));
2975 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2976 type.FullName, dm.MemberSignatures ["C#"]);
2979 oldmember.SetAttribute ("__monodocer-seen__", "true");
2981 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
2982 if (ecmadocs.Name != "Docs")
2983 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
2988 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
2997 abstract class DocumentationImporter {
2999 public abstract void ImportDocumentation (DocsNodeInfo info);
3002 class MsxdocDocumentationImporter : DocumentationImporter {
3004 XmlDocument slashdocs;
3006 public MsxdocDocumentationImporter (string file)
3008 var xml = File.ReadAllText (file);
3010 // Ensure Unix line endings
3011 xml = xml.Replace ("\r", "");
3013 slashdocs = new XmlDocument();
3014 slashdocs.LoadXml (xml);
3017 public override void ImportDocumentation (DocsNodeInfo info)
3019 XmlNode elem = GetDocs (info.Member ?? info.Type);
3024 XmlElement e = info.Node;
3026 if (elem.SelectSingleNode("summary") != null)
3027 MDocUpdater.ClearElement(e, "summary");
3028 if (elem.SelectSingleNode("remarks") != null)
3029 MDocUpdater.ClearElement(e, "remarks");
3030 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3031 MDocUpdater.ClearElement(e, "value");
3032 MDocUpdater.ClearElement(e, "returns");
3035 foreach (XmlNode child in elem.ChildNodes) {
3036 switch (child.Name) {
3039 XmlAttribute name = child.Attributes ["name"];
3042 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3044 p2.InnerXml = child.InnerXml;
3047 // Occasionally XML documentation will use <returns/> on
3048 // properties, so let's try to normalize things.
3051 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3052 v.InnerXml = child.InnerXml;
3058 case "permission": {
3059 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3062 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3064 a = e.OwnerDocument.CreateElement (child.Name);
3065 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
3068 a.InnerXml = child.InnerXml;
3072 XmlAttribute cref = child.Attributes ["cref"];
3075 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3077 a = e.OwnerDocument.CreateElement ("altmember");
3078 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
3085 if (child.NodeType == XmlNodeType.Element &&
3086 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3089 MDocUpdater.CopyNode (child, e);
3096 private XmlNode GetDocs (MemberReference member)
3098 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3099 if (slashdocsig != null)
3100 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3105 class EcmaDocumentationImporter : DocumentationImporter {
3109 public EcmaDocumentationImporter (XmlReader ecmaDocs)
3111 this.ecmadocs = ecmaDocs;
3114 public override void ImportDocumentation (DocsNodeInfo info)
3116 if (!ecmadocs.IsStartElement ("Docs")) {
3120 XmlElement e = info.Node;
3122 int depth = ecmadocs.Depth;
3123 ecmadocs.ReadStartElement ("Docs");
3124 while (ecmadocs.Read ()) {
3125 if (ecmadocs.Name == "Docs") {
3126 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3129 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3131 if (!ecmadocs.IsStartElement ())
3133 switch (ecmadocs.Name) {
3136 string name = ecmadocs.GetAttribute ("name");
3139 XmlNode doc = e.SelectSingleNode (
3140 ecmadocs.Name + "[@name='" + name + "']");
3141 string value = ecmadocs.ReadInnerXml ();
3143 doc.InnerXml = value.Replace ("\r", "");
3150 string name = ecmadocs.Name;
3151 string cref = ecmadocs.GetAttribute ("cref");
3154 XmlNode doc = e.SelectSingleNode (
3155 ecmadocs.Name + "[@cref='" + cref + "']");
3156 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3158 doc.InnerXml = value;
3160 XmlElement n = e.OwnerDocument.CreateElement (name);
3161 n.SetAttribute ("cref", cref);
3168 string name = ecmadocs.Name;
3169 string xpath = ecmadocs.Name;
3170 StringList attributes = new StringList (ecmadocs.AttributeCount);
3171 if (ecmadocs.MoveToFirstAttribute ()) {
3173 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3174 } while (ecmadocs.MoveToNextAttribute ());
3175 ecmadocs.MoveToContent ();
3177 if (attributes.Count > 0) {
3178 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3180 XmlNode doc = e.SelectSingleNode (xpath);
3181 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3183 doc.InnerXml = value;
3186 XmlElement n = e.OwnerDocument.CreateElement (name);
3188 foreach (string a in attributes) {
3189 int eq = a.IndexOf ('=');
3190 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3201 class DocumentationMember {
3202 public StringToStringMap MemberSignatures = new StringToStringMap ();
3203 public string ReturnType;
3204 public StringList Parameters;
3205 public string MemberName;
3206 public string MemberType;
3208 public DocumentationMember (XmlReader reader)
3210 MemberName = reader.GetAttribute ("MemberName");
3211 int depth = reader.Depth;
3213 StringList p = new StringList ();
3215 if (reader.NodeType != XmlNodeType.Element)
3217 switch (reader.Name) {
3218 case "MemberSignature":
3219 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3222 MemberType = reader.ReadElementString ();
3225 if (reader.Depth == depth + 2)
3226 ReturnType = reader.ReadElementString ();
3229 if (reader.Depth == depth + 2)
3230 p.Add (reader.GetAttribute ("Type"));
3233 if (reader.Depth == depth + 1)
3237 } while (go && reader.Read () && reader.Depth >= depth);
3243 public DocumentationMember (XmlNode node)
3245 MemberName = node.Attributes ["MemberName"].Value;
3246 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3247 XmlAttribute l = n.Attributes ["Language"];
3248 XmlAttribute v = n.Attributes ["Value"];
3249 if (l != null && v != null)
3250 MemberSignatures [l.Value] = v.Value;
3252 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3253 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
3255 ReturnType = rt.InnerText;
3256 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
3258 Parameters = new StringList (p.Count);
3259 for (int i = 0; i < p.Count; ++i)
3260 Parameters.Add (p [i].Attributes ["Type"].Value);
3265 public class DynamicParserContext {
3266 public ReadOnlyCollection<bool> TransformFlags;
3267 public int TransformIndex;
3269 public DynamicParserContext (ICustomAttributeProvider provider)
3272 if (provider.HasCustomAttributes &&
3273 (da = (provider.CustomAttributes.Cast<CustomAttribute>()
3274 .SingleOrDefault (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))) != null) {
3275 CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
3276 ? new CustomAttributeArgument [0]
3277 : (CustomAttributeArgument[]) da.ConstructorArguments [0].Value;
3279 TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool) t.Value).ToArray());
3284 public enum MemberFormatterState {
3286 WithinGenericTypeParameters,
3289 public abstract class MemberFormatter {
3291 public virtual string Language {
3295 public string GetName (MemberReference member)
3297 return GetName (member, null);
3300 public virtual string GetName (MemberReference member, DynamicParserContext context)
3302 TypeReference type = member as TypeReference;
3304 return GetTypeName (type, context);
3305 MethodReference method = member as MethodReference;
3306 if (method != null && method.Name == ".ctor") // method.IsConstructor
3307 return GetConstructorName (method);
3309 return GetMethodName (method);
3310 PropertyReference prop = member as PropertyReference;
3312 return GetPropertyName (prop);
3313 FieldReference field = member as FieldReference;
3315 return GetFieldName (field);
3316 EventReference e = member as EventReference;
3318 return GetEventName (e);
3319 throw new NotSupportedException ("Can't handle: " +
3320 (member == null ? "null" : member.GetType().ToString()));
3323 protected virtual string GetTypeName (TypeReference type)
3325 return GetTypeName (type, null);
3328 protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
3331 throw new ArgumentNullException ("type");
3332 return _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
3335 protected virtual char[] ArrayDelimeters {
3336 get {return new char[]{'[', ']'};}
3339 protected virtual MemberFormatterState MemberFormatterState { get; set; }
3341 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3343 if (type is ArrayType) {
3344 TypeSpecification spec = type as TypeSpecification;
3345 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
3346 return AppendArrayModifiers (buf, (ArrayType) type);
3348 if (type is ByReferenceType) {
3349 return AppendRefTypeName (buf, type, context);
3351 if (type is PointerType) {
3352 return AppendPointerTypeName (buf, type, context);
3354 AppendNamespace (buf, type);
3355 if (type is GenericParameter) {
3356 return AppendTypeName (buf, type, context);
3358 GenericInstanceType genInst = type as GenericInstanceType;
3359 if (type.GenericParameters.Count == 0 &&
3360 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3361 return AppendFullTypeName (buf, type, context);
3363 return AppendGenericType (buf, type, context);
3366 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3368 string ns = DocUtils.GetNamespace (type);
3369 if (ns != null && ns.Length > 0)
3370 buf.Append (ns).Append ('.');
3374 protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3376 if (type.DeclaringType != null)
3377 AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
3378 return AppendTypeName (buf, type, context);
3381 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3383 if (context != null)
3384 context.TransformIndex++;
3385 return AppendTypeName (buf, type.Name);
3388 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3390 int n = typename.IndexOf ("`");
3392 return buf.Append (typename.Substring (0, n));
3393 return buf.Append (typename);
3396 protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
3398 buf.Append (ArrayDelimeters [0]);
3399 int rank = array.Rank;
3401 buf.Append (new string (',', rank-1));
3402 return buf.Append (ArrayDelimeters [1]);
3405 protected virtual string RefTypeModifier {
3409 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3411 TypeSpecification spec = type as TypeSpecification;
3412 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
3413 .Append (RefTypeModifier);
3416 protected virtual string PointerModifier {
3420 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3422 TypeSpecification spec = type as TypeSpecification;
3423 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
3424 .Append (PointerModifier);
3427 protected virtual char[] GenericTypeContainer {
3428 get {return new char[]{'<', '>'};}
3431 protected virtual char NestedTypeSeparator {
3435 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
3437 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3438 type is GenericInstanceType ? type.GetElementType () : type);
3439 List<TypeReference> genArgs = GetGenericArguments (type);
3442 bool insertNested = false;
3443 foreach (var decl in decls) {
3444 TypeReference declDef = decl.Resolve () ?? decl;
3446 buf.Append (NestedTypeSeparator);
3448 insertNested = true;
3449 AppendTypeName (buf, declDef, context);
3450 int ac = DocUtils.GetGenericArgumentCount (declDef);
3454 buf.Append (GenericTypeContainer [0]);
3455 var origState = MemberFormatterState;
3456 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3457 _AppendTypeName (buf, genArgs [argIdx++], context);
3458 for (int i = 1; i < c; ++i) {
3459 _AppendTypeName (buf.Append (","), genArgs [argIdx++], context);
3461 MemberFormatterState = origState;
3462 buf.Append (GenericTypeContainer [1]);
3468 protected List<TypeReference> GetGenericArguments (TypeReference type)
3470 var args = new List<TypeReference> ();
3471 GenericInstanceType inst = type as GenericInstanceType;
3473 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
3475 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
3479 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3484 protected virtual string GetConstructorName (MethodReference constructor)
3486 return constructor.Name;
3489 protected virtual string GetMethodName (MethodReference method)
3494 protected virtual string GetPropertyName (PropertyReference property)
3496 return property.Name;
3499 protected virtual string GetFieldName (FieldReference field)
3504 protected virtual string GetEventName (EventReference e)
3509 public virtual string GetDeclaration (MemberReference member)
3512 throw new ArgumentNullException ("member");
3513 TypeDefinition type = member as TypeDefinition;
3515 return GetTypeDeclaration (type);
3516 MethodDefinition method = member as MethodDefinition;
3517 if (method != null && method.IsConstructor)
3518 return GetConstructorDeclaration (method);
3520 return GetMethodDeclaration (method);
3521 PropertyDefinition prop = member as PropertyDefinition;
3523 return GetPropertyDeclaration (prop);
3524 FieldDefinition field = member as FieldDefinition;
3526 return GetFieldDeclaration (field);
3527 EventDefinition e = member as EventDefinition;
3529 return GetEventDeclaration (e);
3530 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3533 protected virtual string GetTypeDeclaration (TypeDefinition type)
3536 throw new ArgumentNullException ("type");
3537 StringBuilder buf = new StringBuilder (type.Name.Length);
3538 _AppendTypeName (buf, type, null);
3539 AppendGenericTypeConstraints (buf, type);
3540 return buf.ToString ();
3543 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
3545 return GetConstructorName (constructor);
3548 protected virtual string GetMethodDeclaration (MethodDefinition method)
3550 if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
3551 ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
3554 // Special signature for destructors.
3555 if (method.Name == "Finalize" && method.Parameters.Count == 0)
3556 return GetFinalizerName (method);
3558 StringBuilder buf = new StringBuilder ();
3560 AppendVisibility (buf, method);
3561 if (buf.Length == 0 &&
3562 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3565 AppendModifiers (buf, method);
3567 if (buf.Length != 0)
3569 buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
3571 AppendMethodName (buf, method);
3572 AppendGenericMethod (buf, method).Append (" ");
3573 AppendParameters (buf, method, method.Parameters);
3574 AppendGenericMethodConstraints (buf, method);
3575 return buf.ToString ();
3578 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3580 return buf.Append (method.Name);
3583 protected virtual string GetFinalizerName (MethodDefinition method)
3588 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3593 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3598 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3603 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
3608 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3613 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
3615 return GetPropertyName (property);
3618 protected virtual string GetFieldDeclaration (FieldDefinition field)
3620 return GetFieldName (field);
3623 protected virtual string GetEventDeclaration (EventDefinition e)
3625 return GetEventName (e);
3629 class ILFullMemberFormatter : MemberFormatter {
3631 public override string Language {
3632 get {return "ILAsm";}
3635 protected override char NestedTypeSeparator {
3641 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3643 if (GetBuiltinType (type.FullName) != null)
3645 string ns = DocUtils.GetNamespace (type);
3646 if (ns != null && ns.Length > 0) {
3647 if (type.IsValueType)
3648 buf.Append ("valuetype ");
3650 buf.Append ("class ");
3651 buf.Append (ns).Append ('.');
3656 private static string GetBuiltinType (string t)
3659 case "System.Byte": return "unsigned int8";
3660 case "System.SByte": return "int8";
3661 case "System.Int16": return "int16";
3662 case "System.Int32": return "int32";
3663 case "System.Int64": return "int64";
3664 case "System.IntPtr": return "native int";
3666 case "System.UInt16": return "unsigned int16";
3667 case "System.UInt32": return "unsigned int32";
3668 case "System.UInt64": return "unsigned int64";
3669 case "System.UIntPtr": return "native unsigned int";
3671 case "System.Single": return "float32";
3672 case "System.Double": return "float64";
3673 case "System.Boolean": return "bool";
3674 case "System.Char": return "char";
3675 case "System.Void": return "void";
3676 case "System.String": return "string";
3677 case "System.Object": return "object";
3682 protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
3684 return buf.Append (typename);
3687 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3689 if (type is GenericParameter) {
3690 AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
3694 string s = GetBuiltinType (type.FullName);
3696 return buf.Append (s);
3698 return base.AppendTypeName (buf, type, context);
3701 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
3703 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
3704 return buf.Append (type.Owner is TypeReference ? "!" : "!!");
3706 GenericParameterAttributes attrs = type.Attributes;
3707 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
3708 buf.Append ("class ");
3709 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
3710 buf.Append ("struct ");
3711 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
3712 buf.Append (".ctor ");
3713 IList<TypeReference> constraints = type.Constraints;
3714 MemberFormatterState = 0;
3715 if (constraints.Count > 0) {
3716 var full = new ILFullMemberFormatter ();
3717 buf.Append ("(").Append (full.GetName (constraints [0]));
3718 for (int i = 1; i < constraints.Count; ++i) {
3719 buf.Append (", ").Append (full.GetName (constraints [i]));
3723 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3725 if ((attrs & GenericParameterAttributes.Covariant) != 0)
3727 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
3732 protected override string GetTypeDeclaration (TypeDefinition type)
3734 string visibility = GetTypeVisibility (type.Attributes);
3735 if (visibility == null)
3738 StringBuilder buf = new StringBuilder ();
3740 buf.Append (".class ");
3742 buf.Append ("nested ");
3743 buf.Append (visibility).Append (" ");
3744 if (type.IsInterface)
3745 buf.Append ("interface ");
3746 if (type.IsSequentialLayout)
3747 buf.Append ("sequential ");
3748 if (type.IsAutoLayout)
3749 buf.Append ("auto ");
3750 if (type.IsAnsiClass)
3751 buf.Append ("ansi ");
3752 if (type.IsAbstract)
3753 buf.Append ("abstract ");
3754 if (type.IsSerializable)
3755 buf.Append ("serializable ");
3757 buf.Append ("sealed ");
3758 if (type.IsBeforeFieldInit)
3759 buf.Append ("beforefieldinit ");
3760 var state = MemberFormatterState;
3761 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3762 buf.Append (GetName (type));
3763 MemberFormatterState = state;
3764 var full = new ILFullMemberFormatter ();
3765 if (type.BaseType != null) {
3766 buf.Append (" extends ");
3767 if (type.BaseType.FullName == "System.Object")
3768 buf.Append ("System.Object");
3770 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
3773 foreach (var name in type.Interfaces
3774 .Select (i => full.GetName (i))
3775 .OrderBy (n => n)) {
3777 buf.Append (" implements ");
3786 return buf.ToString ();
3789 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
3791 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3792 type is GenericInstanceType ? type.GetElementType () : type);
3794 foreach (var decl in decls) {
3795 TypeReference declDef = decl.Resolve () ?? decl;
3797 buf.Append (NestedTypeSeparator);
3800 AppendTypeName (buf, declDef, context);
3804 foreach (TypeReference arg in GetGenericArguments (type)) {
3808 _AppendTypeName (buf, arg, context);
3814 static string GetTypeVisibility (TypeAttributes ta)
3816 switch (ta & TypeAttributes.VisibilityMask) {
3817 case TypeAttributes.Public:
3818 case TypeAttributes.NestedPublic:
3821 case TypeAttributes.NestedFamily:
3822 case TypeAttributes.NestedFamORAssem:
3830 protected override string GetConstructorDeclaration (MethodDefinition constructor)
3832 return GetMethodDeclaration (constructor);
3835 protected override string GetMethodDeclaration (MethodDefinition method)
3837 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
3840 var buf = new StringBuilder ();
3841 buf.Append (".method ");
3842 AppendVisibility (buf, method);
3843 if (method.IsStatic)
3844 buf.Append ("static ");
3845 if (method.IsHideBySig)
3846 buf.Append ("hidebysig ");
3847 if (method.IsPInvokeImpl) {
3848 var info = method.PInvokeInfo;
3849 buf.Append ("pinvokeimpl (\"")
3850 .Append (info.Module.Name)
3851 .Append ("\" as \"")
3852 .Append (info.EntryPoint)
3854 if (info.IsCharSetAuto)
3855 buf.Append (" auto");
3856 if (info.IsCharSetUnicode)
3857 buf.Append (" unicode");
3858 if (info.IsCharSetAnsi)
3859 buf.Append (" ansi");
3860 if (info.IsCallConvCdecl)
3861 buf.Append (" cdecl");
3862 if (info.IsCallConvStdCall)
3863 buf.Append (" stdcall");
3864 if (info.IsCallConvWinapi)
3865 buf.Append (" winapi");
3866 if (info.IsCallConvThiscall)
3867 buf.Append (" thiscall");
3868 if (info.SupportsLastError)
3869 buf.Append (" lasterr");
3872 if (method.IsSpecialName)
3873 buf.Append ("specialname ");
3874 if (method.IsRuntimeSpecialName)
3875 buf.Append ("rtspecialname ");
3876 if (method.IsNewSlot)
3877 buf.Append ("newslot ");
3878 if (method.IsVirtual)
3879 buf.Append ("virtual ");
3880 if (!method.IsStatic)
3881 buf.Append ("instance ");
3882 _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
3884 .Append (method.Name);
3885 if (method.IsGenericMethod ()) {
3886 var state = MemberFormatterState;
3887 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3888 IList<GenericParameter> args = method.GenericParameters;
3889 if (args.Count > 0) {
3891 _AppendTypeName (buf, args [0], null);
3892 for (int i = 1; i < args.Count; ++i)
3893 _AppendTypeName (buf.Append (", "), args [i], null);
3896 MemberFormatterState = state;
3901 for (int i = 0; i < method.Parameters.Count; ++i) {
3905 _AppendTypeName (buf, method.Parameters [i].ParameterType, new DynamicParserContext (method.Parameters [i]));
3907 buf.Append (method.Parameters [i].Name);
3911 buf.Append (" cil");
3912 if (method.IsRuntime)
3913 buf.Append (" runtime");
3914 if (method.IsManaged)
3915 buf.Append (" managed");
3917 return buf.ToString ();
3920 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3922 if (DocUtils.IsExplicitlyImplemented (method)) {
3923 TypeReference iface;
3924 MethodReference ifaceMethod;
3925 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3926 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3928 .Append (ifaceMethod.Name);
3930 return base.AppendMethodName (buf, method);
3933 protected override string RefTypeModifier {
3937 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3939 if (method.IsPublic)
3940 return buf.Append ("public ");
3941 if (method.IsFamilyAndAssembly)
3942 return buf.Append ("familyandassembly");
3943 if (method.IsFamilyOrAssembly)
3944 return buf.Append ("familyorassembly");
3945 if (method.IsFamily)
3946 return buf.Append ("family");
3950 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3952 string modifiers = String.Empty;
3953 if (method.IsStatic) modifiers += " static";
3954 if (method.IsVirtual && !method.IsAbstract) {
3955 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3956 else modifiers += " override";
3958 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
3959 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
3960 if (method.IsFinal) modifiers += " sealed";
3961 if (modifiers == " virtual sealed") modifiers = "";
3963 return buf.Append (modifiers);
3966 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3968 if (method.IsGenericMethod ()) {
3969 IList<GenericParameter> args = method.GenericParameters;
3970 if (args.Count > 0) {
3972 buf.Append (args [0].Name);
3973 for (int i = 1; i < args.Count; ++i)
3974 buf.Append (",").Append (args [i].Name);
3981 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
3983 return AppendParameters (buf, method, parameters, '(', ')');
3986 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
3990 if (parameters.Count > 0) {
3991 if (DocUtils.IsExtensionMethod (method))
3992 buf.Append ("this ");
3993 AppendParameter (buf, parameters [0]);
3994 for (int i = 1; i < parameters.Count; ++i) {
3996 AppendParameter (buf, parameters [i]);
4000 return buf.Append (end);
4003 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4005 if (parameter.ParameterType is ByReferenceType) {
4006 if (parameter.IsOut)
4007 buf.Append ("out ");
4009 buf.Append ("ref ");
4011 buf.Append (GetName (parameter.ParameterType)).Append (" ");
4012 return buf.Append (parameter.Name);
4015 protected override string GetPropertyDeclaration (PropertyDefinition property)
4017 MethodDefinition gm = null, sm = null;
4019 string get_visible = null;
4020 if ((gm = property.GetMethod) != null &&
4021 (DocUtils.IsExplicitlyImplemented (gm) ||
4022 (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
4023 get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
4024 string set_visible = null;
4025 if ((sm = property.SetMethod) != null &&
4026 (DocUtils.IsExplicitlyImplemented (sm) ||
4027 (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
4028 set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
4030 if ((set_visible == null) && (get_visible == null))
4033 StringBuilder buf = new StringBuilder ()
4034 .Append (".property ");
4035 if (!(gm ?? sm).IsStatic)
4036 buf.Append ("instance ");
4037 _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
4038 buf.Append (' ').Append (property.Name);
4039 if (!property.HasParameters || property.Parameters.Count == 0)
4040 return buf.ToString ();
4044 foreach (ParameterDefinition p in property.Parameters) {
4048 _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
4052 return buf.ToString ();
4055 protected override string GetFieldDeclaration (FieldDefinition field)
4057 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4058 if (declType.IsEnum && field.Name == "value__")
4059 return null; // This member of enums aren't documented.
4061 StringBuilder buf = new StringBuilder ();
4062 AppendFieldVisibility (buf, field);
4063 if (buf.Length == 0)
4066 buf.Insert (0, ".field ");
4069 buf.Append ("static ");
4070 if (field.IsInitOnly)
4071 buf.Append ("initonly ");
4072 if (field.IsLiteral)
4073 buf.Append ("literal ");
4074 _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
4075 buf.Append (' ').Append (field.Name);
4076 AppendFieldValue (buf, field);
4078 return buf.ToString ();
4081 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4084 return buf.Append ("public ");
4085 if (field.IsFamilyAndAssembly)
4086 return buf.Append ("familyandassembly ");
4087 if (field.IsFamilyOrAssembly)
4088 return buf.Append ("familyorassembly ");
4090 return buf.Append ("family ");
4094 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4096 // enums have a value__ field, which we ignore
4097 if (field.DeclaringType.IsGenericType ())
4099 if (field.HasConstant && field.IsLiteral) {
4102 val = field.Constant;
4107 buf.Append (" = ").Append ("null");
4108 else if (val is Enum)
4110 .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4112 .Append (val.ToString ())
4114 else if (val is IFormattable) {
4115 string value = ((IFormattable)val).ToString();
4118 buf.Append ("\"" + value + "\"");
4120 buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4129 protected override string GetEventDeclaration (EventDefinition e)
4131 StringBuilder buf = new StringBuilder ();
4132 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4137 buf.Append (".event ")
4138 .Append (GetName (e.EventType))
4142 return buf.ToString ();
4146 class ILMemberFormatter : ILFullMemberFormatter {
4147 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4153 class CSharpFullMemberFormatter : MemberFormatter {
4155 public override string Language {
4159 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4161 string ns = DocUtils.GetNamespace (type);
4162 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
4163 buf.Append (ns).Append ('.');
4167 private string GetCSharpType (string t)
4170 case "System.Byte": return "byte";
4171 case "System.SByte": return "sbyte";
4172 case "System.Int16": return "short";
4173 case "System.Int32": return "int";
4174 case "System.Int64": return "long";
4176 case "System.UInt16": return "ushort";
4177 case "System.UInt32": return "uint";
4178 case "System.UInt64": return "ulong";
4180 case "System.Single": return "float";
4181 case "System.Double": return "double";
4182 case "System.Decimal": return "decimal";
4183 case "System.Boolean": return "bool";
4184 case "System.Char": return "char";
4185 case "System.Void": return "void";
4186 case "System.String": return "string";
4187 case "System.Object": return "object";
4192 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4194 if (context != null && context.TransformFlags != null &&
4195 (context.TransformFlags.Count == 0 || context.TransformFlags [context.TransformIndex])) {
4196 context.TransformIndex++;
4197 return buf.Append ("dynamic");
4200 if (type is GenericParameter)
4201 return AppendGenericParameterConstraints (buf, (GenericParameter) type, context).Append (type.Name);
4202 string t = type.FullName;
4203 if (!t.StartsWith ("System.")) {
4204 return base.AppendTypeName (buf, type, context);
4207 string s = GetCSharpType (t);
4209 if (context != null)
4210 context.TransformIndex++;
4211 return buf.Append (s);
4214 return base.AppendTypeName (buf, type, context);
4217 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
4219 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
4221 GenericParameterAttributes attrs = type.Attributes;
4222 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
4223 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
4227 buf.Append ("out ");
4231 protected override string GetTypeDeclaration (TypeDefinition type)
4233 string visibility = GetTypeVisibility (type.Attributes);
4234 if (visibility == null)
4237 StringBuilder buf = new StringBuilder ();
4239 buf.Append (visibility);
4242 MemberFormatter full = new CSharpFullMemberFormatter ();
4244 if (DocUtils.IsDelegate (type)) {
4245 buf.Append("delegate ");
4246 MethodDefinition invoke = type.GetMethod ("Invoke");
4247 buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
4248 buf.Append (GetName (type));
4249 AppendParameters (buf, invoke, invoke.Parameters);
4250 AppendGenericTypeConstraints (buf, type);
4253 return buf.ToString();
4256 if (type.IsAbstract && !type.IsInterface)
4257 buf.Append("abstract ");
4258 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
4259 buf.Append("sealed ");
4260 buf.Replace ("abstract sealed", "static");
4262 buf.Append (GetTypeKind (type));
4264 buf.Append (GetCSharpType (type.FullName) == null
4269 TypeReference basetype = type.BaseType;
4270 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
4273 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
4274 .Select (iface => full.GetName (iface))
4278 if (basetype != null || interface_names.Count > 0)
4281 if (basetype != null) {
4282 buf.Append (full.GetName (basetype));
4283 if (interface_names.Count > 0)
4287 for (int i = 0; i < interface_names.Count; i++){
4290 buf.Append (interface_names [i]);
4292 AppendGenericTypeConstraints (buf, type);
4295 return buf.ToString ();
4298 static string GetTypeKind (TypeDefinition t)
4304 if (t.IsClass || t.FullName == "System.Enum")
4308 throw new ArgumentException(t.FullName);
4311 static string GetTypeVisibility (TypeAttributes ta)
4313 switch (ta & TypeAttributes.VisibilityMask) {
4314 case TypeAttributes.Public:
4315 case TypeAttributes.NestedPublic:
4318 case TypeAttributes.NestedFamily:
4319 case TypeAttributes.NestedFamORAssem:
4327 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4329 if (type.GenericParameters.Count == 0)
4331 return AppendConstraints (buf, type.GenericParameters);
4334 private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
4336 foreach (GenericParameter genArg in genArgs) {
4337 GenericParameterAttributes attrs = genArg.Attributes;
4338 IList<TypeReference> constraints = genArg.Constraints;
4339 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
4342 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
4343 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
4344 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
4347 if (!isref && !isvt && !isnew && constraints.Count == 0)
4349 buf.Append (" where ").Append (genArg.Name).Append (" : ");
4351 buf.Append ("class");
4355 buf.Append ("struct");
4358 if (constraints.Count > 0 && !isvt) {
4361 buf.Append (GetTypeName (constraints [0]));
4362 for (int i = 1; i < constraints.Count; ++i)
4363 buf.Append (", ").Append (GetTypeName (constraints [i]));
4365 if (isnew && !isvt) {
4368 buf.Append ("new()");
4374 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4376 StringBuilder buf = new StringBuilder ();
4377 AppendVisibility (buf, constructor);
4378 if (buf.Length == 0)
4382 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
4383 AppendParameters (buf, constructor, constructor.Parameters);
4386 return buf.ToString ();
4389 protected override string GetMethodDeclaration (MethodDefinition method)
4391 string decl = base.GetMethodDeclaration (method);
4397 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4399 if (DocUtils.IsExplicitlyImplemented (method)) {
4400 TypeReference iface;
4401 MethodReference ifaceMethod;
4402 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4403 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4405 .Append (ifaceMethod.Name);
4407 return base.AppendMethodName (buf, method);
4410 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4412 if (method.GenericParameters.Count == 0)
4414 return AppendConstraints (buf, method.GenericParameters);
4417 protected override string RefTypeModifier {
4421 protected override string GetFinalizerName (MethodDefinition method)
4423 return "~" + method.DeclaringType.Name + " ()";
4426 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4430 if (method.IsPublic)
4431 return buf.Append ("public");
4432 if (method.IsFamily || method.IsFamilyOrAssembly)
4433 return buf.Append ("protected");
4437 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4439 string modifiers = String.Empty;
4440 if (method.IsStatic) modifiers += " static";
4441 if (method.IsVirtual && !method.IsAbstract) {
4442 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4443 else modifiers += " override";
4445 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4446 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4447 if (method.IsFinal) modifiers += " sealed";
4448 if (modifiers == " virtual sealed") modifiers = "";
4450 return buf.Append (modifiers);
4453 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4455 if (method.IsGenericMethod ()) {
4456 IList<GenericParameter> args = method.GenericParameters;
4457 if (args.Count > 0) {
4459 buf.Append (args [0].Name);
4460 for (int i = 1; i < args.Count; ++i)
4461 buf.Append (",").Append (args [i].Name);
4468 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4470 return AppendParameters (buf, method, parameters, '(', ')');
4473 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4477 if (parameters.Count > 0) {
4478 if (DocUtils.IsExtensionMethod (method))
4479 buf.Append ("this ");
4480 AppendParameter (buf, parameters [0]);
4481 for (int i = 1; i < parameters.Count; ++i) {
4483 AppendParameter (buf, parameters [i]);
4487 return buf.Append (end);
4490 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4492 if (parameter.ParameterType is ByReferenceType) {
4493 if (parameter.IsOut)
4494 buf.Append ("out ");
4496 buf.Append ("ref ");
4498 buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
4499 buf.Append (parameter.Name);
4500 if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant) {
4501 buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
4506 protected override string GetPropertyDeclaration (PropertyDefinition property)
4508 MethodDefinition method;
4510 string get_visible = null;
4511 if ((method = property.GetMethod) != null &&
4512 (DocUtils.IsExplicitlyImplemented (method) ||
4513 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
4514 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
4515 string set_visible = null;
4516 if ((method = property.SetMethod) != null &&
4517 (DocUtils.IsExplicitlyImplemented (method) ||
4518 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
4519 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
4521 if ((set_visible == null) && (get_visible == null))
4525 StringBuilder buf = new StringBuilder ();
4526 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
4527 buf.Append (visibility = get_visible);
4528 else if (set_visible != null && get_visible == null)
4529 buf.Append (visibility = set_visible);
4531 buf.Append (visibility = "public");
4533 // Pick an accessor to use for static/virtual/override/etc. checks.
4534 method = property.SetMethod;
4536 method = property.GetMethod;
4538 string modifiers = String.Empty;
4539 if (method.IsStatic) modifiers += " static";
4540 if (method.IsVirtual && !method.IsAbstract) {
4541 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
4542 modifiers += " virtual";
4544 modifiers += " override";
4546 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
4547 if (method.IsAbstract && !declDef.IsInterface)
4548 modifiers += " abstract";
4550 modifiers += " sealed";
4551 if (modifiers == " virtual sealed")
4553 buf.Append (modifiers).Append (' ');
4555 buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
4557 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
4558 string name = property.Name;
4559 foreach (MemberReference mi in defs) {
4560 if (mi == property) {
4565 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
4567 if (property.Parameters.Count != 0) {
4568 AppendParameters (buf, method, property.Parameters, '[', ']');
4572 if (get_visible != null) {
4573 if (get_visible != visibility)
4574 buf.Append (' ').Append (get_visible);
4575 buf.Append (" get;");
4577 if (set_visible != null) {
4578 if (set_visible != visibility)
4579 buf.Append (' ').Append (set_visible);
4580 buf.Append (" set;");
4584 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
4587 protected override string GetFieldDeclaration (FieldDefinition field)
4589 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4590 if (declType.IsEnum && field.Name == "value__")
4591 return null; // This member of enums aren't documented.
4593 StringBuilder buf = new StringBuilder ();
4594 AppendFieldVisibility (buf, field);
4595 if (buf.Length == 0)
4598 if (declType.IsEnum)
4601 if (field.IsStatic && !field.IsLiteral)
4602 buf.Append (" static");
4603 if (field.IsInitOnly)
4604 buf.Append (" readonly");
4605 if (field.IsLiteral)
4606 buf.Append (" const");
4608 buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
4609 buf.Append (field.Name);
4610 AppendFieldValue (buf, field);
4613 return buf.ToString ();
4616 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4619 return buf.Append ("public");
4620 if (field.IsFamily || field.IsFamilyOrAssembly)
4621 return buf.Append ("protected");
4625 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4627 // enums have a value__ field, which we ignore
4628 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
4629 field.DeclaringType.IsGenericType ())
4631 if (field.HasConstant && field.IsLiteral) {
4634 val = field.Constant;
4639 buf.Append (" = ").Append ("null");
4640 else if (val is Enum)
4641 buf.Append (" = ").Append (val.ToString ());
4642 else if (val is IFormattable) {
4643 string value = ((IFormattable)val).ToString();
4645 value = "\"" + value + "\"";
4646 buf.Append (" = ").Append (value);
4652 protected override string GetEventDeclaration (EventDefinition e)
4654 StringBuilder buf = new StringBuilder ();
4655 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4659 AppendModifiers (buf, e.AddMethod);
4661 buf.Append (" event ");
4662 buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters [0]))).Append (' ');
4663 buf.Append (e.Name).Append (';');
4665 return buf.ToString ();
4669 class CSharpMemberFormatter : CSharpFullMemberFormatter {
4670 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4676 class DocTypeFullMemberFormatter : MemberFormatter {
4677 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
4679 protected override char NestedTypeSeparator {
4684 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
4685 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4691 class SlashDocMemberFormatter : MemberFormatter {
4693 protected override char[] GenericTypeContainer {
4694 get {return new char[]{'{', '}'};}
4697 private bool AddTypeCount = true;
4699 private TypeReference genDeclType;
4700 private MethodReference genDeclMethod;
4702 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4704 if (type is GenericParameter) {
4706 if (genDeclType != null) {
4707 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
4708 for (int i = 0; i < genArgs.Count; ++i) {
4709 if (genArgs [i].Name == type.Name) {
4710 buf.Append ('`').Append (i);
4715 if (genDeclMethod != null) {
4716 IList<GenericParameter> genArgs = null;
4717 if (genDeclMethod.IsGenericMethod ()) {
4718 genArgs = genDeclMethod.GenericParameters;
4719 for (int i = 0; i < genArgs.Count; ++i) {
4720 if (genArgs [i].Name == type.Name) {
4721 buf.Append ("``").Append (i);
4727 if (genDeclType == null && genDeclMethod == null) {
4728 // Probably from within an explicitly implemented interface member,
4729 // where CSC uses parameter names instead of indices (why?), e.g.
4730 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
4731 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
4732 buf.Append (type.Name);
4734 if (buf.Length == l) {
4735 throw new Exception (string.Format (
4736 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
4737 type.Name, genDeclType, genDeclMethod));
4741 base.AppendTypeName (buf, type, context);
4743 int numArgs = type.GenericParameters.Count;
4744 if (type.DeclaringType != null)
4745 numArgs -= type.GenericParameters.Count;
4747 buf.Append ('`').Append (numArgs);
4754 protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
4756 buf.Append (ArrayDelimeters [0]);
4757 int rank = array.Rank;
4760 for (int i = 1; i < rank; ++i) {
4764 return buf.Append (ArrayDelimeters [1]);
4767 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4770 base.AppendGenericType (buf, type, context);
4772 AppendType (buf, type, context);
4776 private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4778 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
4779 bool insertNested = false;
4780 int prevParamCount = 0;
4781 foreach (var decl in decls) {
4783 buf.Append (NestedTypeSeparator);
4784 insertNested = true;
4785 base.AppendTypeName (buf, decl, context);
4786 int argCount = DocUtils.GetGenericArgumentCount (decl);
4787 int numArgs = argCount - prevParamCount;
4788 prevParamCount = argCount;
4790 buf.Append ('`').Append (numArgs);
4795 public override string GetDeclaration (MemberReference member)
4797 TypeReference r = member as TypeReference;
4799 return "T:" + GetTypeName (r);
4801 return base.GetDeclaration (member);
4804 protected override string GetConstructorName (MethodReference constructor)
4806 return GetMethodDefinitionName (constructor, "#ctor");
4809 protected override string GetMethodName (MethodReference method)
4812 MethodDefinition methodDef = method as MethodDefinition;
4813 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
4816 TypeReference iface;
4817 MethodReference ifaceMethod;
4818 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
4819 AddTypeCount = false;
4820 name = GetTypeName (iface) + "." + ifaceMethod.Name;
4821 AddTypeCount = true;
4823 return GetMethodDefinitionName (method, name);
4826 private string GetMethodDefinitionName (MethodReference method, string name)
4828 StringBuilder buf = new StringBuilder ();
4829 buf.Append (GetTypeName (method.DeclaringType));
4831 buf.Append (name.Replace (".", "#"));
4832 if (method.IsGenericMethod ()) {
4833 IList<GenericParameter> genArgs = method.GenericParameters;
4834 if (genArgs.Count > 0)
4835 buf.Append ("``").Append (genArgs.Count);
4837 IList<ParameterDefinition> parameters = method.Parameters;
4839 genDeclType = method.DeclaringType;
4840 genDeclMethod = method;
4841 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
4845 genDeclMethod = null;
4847 return buf.ToString ();
4850 private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
4852 if (parameters.Count == 0)
4857 AppendParameter (buf, genArgs, parameters [0]);
4858 for (int i = 1; i < parameters.Count; ++i) {
4860 AppendParameter (buf, genArgs, parameters [i]);
4863 return buf.Append (')');
4866 private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
4868 AddTypeCount = false;
4869 buf.Append (GetTypeName (parameter.ParameterType));
4870 AddTypeCount = true;
4874 protected override string GetPropertyName (PropertyReference property)
4878 PropertyDefinition propertyDef = property as PropertyDefinition;
4879 MethodDefinition method = null;
4880 if (propertyDef != null)
4881 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
4882 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
4883 name = property.Name;
4885 TypeReference iface;
4886 MethodReference ifaceMethod;
4887 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4888 AddTypeCount = false;
4889 name = string.Join ("#", new string[]{
4890 GetTypeName (iface).Replace (".", "#"),
4891 DocUtils.GetMember (property.Name)
4893 AddTypeCount = true;
4896 StringBuilder buf = new StringBuilder ();
4897 buf.Append (GetName (property.DeclaringType));
4900 IList<ParameterDefinition> parameters = property.Parameters;
4901 if (parameters.Count > 0) {
4902 genDeclType = property.DeclaringType;
4904 IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
4905 AppendParameter (buf, genArgs, parameters [0]);
4906 for (int i = 1; i < parameters.Count; ++i) {
4908 AppendParameter (buf, genArgs, parameters [i]);
4913 return buf.ToString ();
4916 protected override string GetFieldName (FieldReference field)
4918 return string.Format ("{0}.{1}",
4919 GetName (field.DeclaringType), field.Name);
4922 protected override string GetEventName (EventReference e)
4924 return string.Format ("{0}.{1}",
4925 GetName (e.DeclaringType), e.Name);
4928 protected override string GetTypeDeclaration (TypeDefinition type)
4930 string name = GetName (type);
4936 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4938 string name = GetName (constructor);
4944 protected override string GetMethodDeclaration (MethodDefinition method)
4946 string name = GetName (method);
4949 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4950 genDeclType = method.DeclaringType;
4951 genDeclMethod = method;
4952 name += "~" + GetName (method.ReturnType);
4954 genDeclMethod = null;
4959 protected override string GetPropertyDeclaration (PropertyDefinition property)
4961 string name = GetName (property);
4967 protected override string GetFieldDeclaration (FieldDefinition field)
4969 string name = GetName (field);
4975 protected override string GetEventDeclaration (EventDefinition e)
4977 string name = GetName (e);
4984 class FileNameMemberFormatter : SlashDocMemberFormatter {
4985 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4990 protected override char NestedTypeSeparator {