1 // Updater program for syncing Mono's ECMA-style documentation files
3 // By Joshua Tauberer <tauberer@for.net>
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Diagnostics;
9 using System.Globalization;
14 using System.Xml.XPath;
19 using MyXmlNodeList = System.Collections.Generic.List<System.Xml.XmlNode>;
20 using StringList = System.Collections.Generic.List<string>;
21 using StringToStringMap = System.Collections.Generic.Dictionary<string, string>;
22 using StringToXmlNodeMap = System.Collections.Generic.Dictionary<string, System.Xml.XmlNode>;
24 namespace Mono.Documentation {
26 class MDocUpdater : MDocCommand
29 List<AssemblyDefinition> assemblies;
30 readonly DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver();
34 bool no_assembly_versions, ignore_missing_types;
35 ExceptionLocations? exceptions;
37 internal int additions = 0, deletions = 0;
39 List<DocumentationImporter> importers = new List<DocumentationImporter> ();
41 DocumentationEnumerator docEnum;
45 static readonly MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
46 static readonly MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
48 static MemberFormatter[] typeFormatters = new MemberFormatter[]{
49 new CSharpMemberFormatter (),
50 new ILMemberFormatter (),
53 static MemberFormatter[] memberFormatters = new MemberFormatter[]{
54 new CSharpFullMemberFormatter (),
55 new ILFullMemberFormatter (),
58 internal static readonly MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
60 MyXmlNodeList extensionMethods = new MyXmlNodeList ();
62 public override void Run (IEnumerable<string> args)
64 show_exceptions = DebugOutput;
66 var types = new List<string> ();
67 var p = new OptionSet () {
69 "Delete removed members from the XML files.",
70 v => delete = v != null },
72 "Document potential exceptions that members can generate. {SOURCES} " +
73 "is a comma-separated list of:\n" +
74 " asm Method calls in same assembly\n" +
75 " depasm Method calls in dependent assemblies\n" +
76 " all Record all possible exceptions\n" +
77 " added Modifier; only create <exception/>s\n" +
78 " for NEW types/members\n" +
79 "If nothing is specified, then only exceptions from the member will " +
81 v => exceptions = ParseExceptionLocations (v) },
83 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
86 case "ignore-missing-types":
87 ignore_missing_types = true;
89 case "no-assembly-versions":
90 no_assembly_versions = true;
93 throw new Exception ("Unsupported flag `" + v + "'.");
96 { "fignore-missing-types",
97 "Do not report an error if a --type=TYPE type\nwas not found.",
98 v => ignore_missing_types = v != null },
99 { "fno-assembly-versions",
100 "Do not generate //AssemblyVersion elements.",
101 v => no_assembly_versions = v != null },
103 "Import documentation from {FILE}.",
104 v => AddImporter (v) },
106 "Check for assembly references in {DIRECTORY}.",
107 v => assemblyResolver.AddSearchDirectory (v) },
109 "Ignored for compatibility with update-ecma-xml.",
112 "Root {DIRECTORY} to generate/update documentation.",
115 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
116 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
117 v => assemblyResolver.AddSearchDirectory (Path.GetDirectoryName (v)) },
119 "Manually specify the assembly {VERSION} that new members were added in.",
122 "Only update documentation for {TYPE}.",
123 v => types.Add (v) },
125 var assemblies = Parse (p, args, "update",
126 "[OPTIONS]+ ASSEMBLIES",
127 "Create or update documentation from ASSEMBLIES.");
128 if (assemblies == null)
130 if (assemblies.Count == 0)
131 Error ("No assemblies specified.");
133 foreach (var dir in assemblies
134 .Where (a => a.Contains (Path.DirectorySeparatorChar))
135 .Select (a => Path.GetDirectoryName (a)))
136 assemblyResolver.AddSearchDirectory (dir);
138 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
141 throw new InvalidOperationException("The --out option is required.");
143 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
145 docEnum = docEnum ?? new DocumentationEnumerator ();
147 // PERFORM THE UPDATES
149 if (types.Count > 0) {
151 DoUpdateTypes (srcPath, types, srcPath);
154 else if (opts.@namespace != null)
155 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
156 Path.Combine (dest_dir, opts.@namespace));
159 DoUpdateAssemblies (srcPath, srcPath);
161 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
164 void AddImporter (string path)
167 XmlReader r = new XmlTextReader (path);
169 while (r.NodeType != XmlNodeType.Element) {
171 Error ("Unable to read XML file: {0}.", path);
173 if (r.LocalName == "doc") {
174 importers.Add (new MsxdocDocumentationImporter (path));
176 else if (r.LocalName == "Libraries") {
177 var ecmadocs = new XmlTextReader (path);
178 docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
179 importers.Add (new EcmaDocumentationImporter (ecmadocs));
182 Error ("Unsupported XML format within {0}.", path);
185 } catch (Exception e) {
186 Environment.ExitCode = 1;
187 Error ("Could not load XML file: {0}.", e.Message);
191 static ExceptionLocations ParseExceptionLocations (string s)
193 ExceptionLocations loc = ExceptionLocations.Member;
196 foreach (var type in s.Split (',')) {
198 case "added": loc |= ExceptionLocations.AddedMembers; break;
199 case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
200 case "asm": loc |= ExceptionLocations.Assembly; break;
201 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
202 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
208 internal void Warning (string format, params object[] args)
210 Message (TraceLevel.Warning, "mdoc: " + format, args);
213 private AssemblyDefinition LoadAssembly (string name)
215 AssemblyDefinition assembly = null;
217 assembly = 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> ();
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 (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 (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.Constructor.DeclaringType.FullName;
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 private 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 () + "\"";
1933 return (bool)v ? "true" : "false";
1934 TypeDefinition valueDef = valueType.Resolve ();
1935 if (valueDef == null || !valueDef.IsEnum)
1936 return v.ToString ();
1937 string typename = GetDocTypeFullName (valueType);
1938 var values = GetEnumerationValues (valueDef);
1939 long c = ToInt64 (v);
1940 if (values.ContainsKey (c))
1941 return typename + "." + values [c];
1942 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
1943 return string.Join (" | ",
1944 (from i in values.Keys
1946 select typename + "." + values [i])
1949 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
1952 private static Dictionary<long, string> GetEnumerationValues (TypeDefinition type)
1954 var values = new Dictionary<long, string> ();
1956 (from f in type.Fields
1957 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
1959 values [ToInt64 (f.Constant)] = f.Name;
1964 static long ToInt64 (object value)
1967 return (long) (ulong) value;
1968 return Convert.ToInt64 (value);
1971 private void MakeParameters (XmlElement root, IList<ParameterDefinition> parameters)
1973 XmlElement e = WriteElement(root, "Parameters");
1975 foreach (ParameterDefinition p in parameters) {
1976 XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
1978 pe.SetAttribute("Name", p.Name);
1979 pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
1980 if (p.ParameterType is ByReferenceType) {
1981 if (p.IsOut) pe.SetAttribute("RefType", "out");
1982 else pe.SetAttribute("RefType", "ref");
1984 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
1988 private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams)
1990 if (typeParams == null || typeParams.Count == 0) {
1991 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
1993 root.RemoveChild (f);
1996 XmlElement e = WriteElement(root, "TypeParameters");
1998 foreach (GenericParameter t in typeParams) {
1999 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2001 pe.SetAttribute("Name", t.Name);
2002 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""));
2003 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2004 IList<TypeReference> constraints = t.Constraints;
2005 GenericParameterAttributes attrs = t.Attributes;
2006 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2014 ce = root.OwnerDocument.CreateElement ("Constraints");
2016 pe.AppendChild (ce);
2017 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2018 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2019 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2020 AppendElementText (ce, "ParameterAttribute", "Covariant");
2021 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2022 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2023 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2024 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2025 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2026 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2027 foreach (TypeReference c in constraints) {
2028 TypeDefinition cd = c.Resolve ();
2029 AppendElementText (ce,
2030 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2031 GetDocTypeFullName (c));
2036 private void MakeParameters (XmlElement root, MemberReference mi)
2038 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2039 MakeParameters (root, ((MethodDefinition)mi).Parameters);
2040 else if (mi is MethodDefinition) {
2041 MethodDefinition mb = (MethodDefinition) mi;
2042 IList<ParameterDefinition> parameters = mb.Parameters;
2043 MakeParameters(root, parameters);
2044 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2045 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2046 p.SetAttribute ("RefType", "this");
2049 else if (mi is PropertyDefinition) {
2050 IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
2051 if (parameters.Count > 0)
2052 MakeParameters(root, parameters);
2056 else if (mi is FieldDefinition) return;
2057 else if (mi is EventDefinition) return;
2058 else throw new ArgumentException();
2061 internal static string GetDocParameterType (TypeReference type)
2063 return GetDocTypeFullName (type).Replace ("@", "&");
2066 private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes)
2068 XmlElement e = WriteElement(root, "ReturnValue");
2070 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2071 if (attributes != null)
2072 MakeAttributes(e, GetCustomAttributes (attributes, ""));
2075 private void MakeReturnValue (XmlElement root, MemberReference mi)
2077 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2079 else if (mi is MethodDefinition)
2080 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes);
2081 else if (mi is PropertyDefinition)
2082 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
2083 else if (mi is FieldDefinition)
2084 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
2085 else if (mi is EventDefinition)
2086 MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
2088 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2091 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2093 MemberReference mi = info.Member;
2094 if (mi is TypeDefinition) return null;
2096 string sigs = memberFormatters [0].GetDeclaration (mi);
2097 if (sigs == null) return null; // not publicly visible
2099 // no documentation for property/event accessors. Is there a better way of doing this?
2100 if (mi.Name.StartsWith("get_")) return null;
2101 if (mi.Name.StartsWith("set_")) return null;
2102 if (mi.Name.StartsWith("add_")) return null;
2103 if (mi.Name.StartsWith("remove_")) return null;
2104 if (mi.Name.StartsWith("raise_")) return null;
2106 XmlElement me = doc.CreateElement("Member");
2107 me.SetAttribute("MemberName", GetMemberName (mi));
2111 if (exceptions.HasValue &&
2112 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2113 UpdateExceptions (info.Node, info.Member);
2115 if (since != null) {
2116 XmlNode docs = me.SelectSingleNode("Docs");
2117 docs.AppendChild (CreateSinceNode (doc));
2123 internal static string GetMemberName (MemberReference mi)
2125 MethodDefinition mb = mi as MethodDefinition;
2127 PropertyDefinition pi = mi as PropertyDefinition;
2130 return DocUtils.GetPropertyName (pi);
2132 StringBuilder sb = new StringBuilder (mi.Name.Length);
2133 if (!DocUtils.IsExplicitlyImplemented (mb))
2134 sb.Append (mi.Name);
2136 TypeReference iface;
2137 MethodReference ifaceMethod;
2138 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2139 sb.Append (GetDocTypeFullName (iface));
2141 sb.Append (ifaceMethod.Name);
2143 if (mb.IsGenericMethod ()) {
2144 IList<GenericParameter> typeParams = mb.GenericParameters;
2145 if (typeParams.Count > 0) {
2147 sb.Append (typeParams [0].Name);
2148 for (int i = 1; i < typeParams.Count; ++i)
2149 sb.Append (",").Append (typeParams [i].Name);
2153 return sb.ToString ();
2156 /// SIGNATURE GENERATION FUNCTIONS
2157 internal static bool IsPrivate (MemberReference mi)
2159 return memberFormatters [0].GetDeclaration (mi) == null;
2162 internal static string GetMemberType (MemberReference mi)
2164 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2165 return "Constructor";
2166 if (mi is MethodDefinition)
2168 if (mi is PropertyDefinition)
2170 if (mi is FieldDefinition)
2172 if (mi is EventDefinition)
2174 throw new ArgumentException();
2177 private static string GetDocTypeName (TypeReference type)
2179 return docTypeFormatter.GetName (type);
2182 internal static string GetDocTypeFullName (TypeReference type)
2184 return DocTypeFullMemberFormatter.Default.GetName (type);
2187 internal static string GetXPathForMember (DocumentationMember member)
2189 StringBuilder xpath = new StringBuilder ();
2190 xpath.Append ("//Members/Member[@MemberName=\"")
2191 .Append (member.MemberName)
2193 if (member.Parameters != null && member.Parameters.Count > 0) {
2194 xpath.Append ("/Parameters[count(Parameter) = ")
2195 .Append (member.Parameters.Count);
2196 for (int i = 0; i < member.Parameters.Count; ++i) {
2197 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2198 xpath.Append (member.Parameters [i]);
2199 xpath.Append ("\"");
2201 xpath.Append ("]/..");
2203 return xpath.ToString ();
2206 public static string GetXPathForMember (XPathNavigator member)
2208 StringBuilder xpath = new StringBuilder ();
2209 xpath.Append ("//Type[@FullName=\"")
2210 .Append (member.SelectSingleNode ("../../@FullName").Value)
2212 xpath.Append ("Members/Member[@MemberName=\"")
2213 .Append (member.SelectSingleNode ("@MemberName").Value)
2215 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2216 if (parameters.Count > 0) {
2217 xpath.Append ("/Parameters[count(Parameter) = ")
2218 .Append (parameters.Count);
2220 while (parameters.MoveNext ()) {
2222 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2223 xpath.Append (parameters.Current.Value);
2224 xpath.Append ("\"");
2226 xpath.Append ("]/..");
2228 return xpath.ToString ();
2231 public static string GetXPathForMember (MemberReference member)
2233 StringBuilder xpath = new StringBuilder ();
2234 xpath.Append ("//Type[@FullName=\"")
2235 .Append (member.DeclaringType.FullName)
2237 xpath.Append ("Members/Member[@MemberName=\"")
2238 .Append (GetMemberName (member))
2241 IList<ParameterDefinition> parameters = null;
2242 if (member is MethodDefinition)
2243 parameters = ((MethodDefinition) member).Parameters;
2244 else if (member is PropertyDefinition) {
2245 parameters = ((PropertyDefinition) member).Parameters;
2247 if (parameters != null && parameters.Count > 0) {
2248 xpath.Append ("/Parameters[count(Parameter) = ")
2249 .Append (parameters.Count);
2250 for (int i = 0; i < parameters.Count; ++i) {
2251 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2252 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2253 xpath.Append ("\"");
2255 xpath.Append ("]/..");
2257 return xpath.ToString ();
2261 static class CecilExtensions {
2262 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
2264 foreach (var c in type.Methods.Where (m => m.IsConstructor))
2265 yield return (MemberReference) c;
2266 foreach (var e in type.Events)
2267 yield return (MemberReference) e;
2268 foreach (var f in type.Fields)
2269 yield return (MemberReference) f;
2270 foreach (var m in type.Methods.Where (m => !m.IsConstructor))
2271 yield return (MemberReference) m;
2272 foreach (var t in type.NestedTypes)
2273 yield return (MemberReference) t;
2274 foreach (var p in type.Properties)
2275 yield return (MemberReference) p;
2278 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
2280 return GetMembers (type).Where (m => m.Name == member);
2283 public static MemberReference GetMember (this TypeDefinition type, string member)
2285 return GetMembers (type, member).EnsureZeroOrOne ();
2288 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2290 if (source.Count () > 1)
2291 throw new InvalidOperationException ("too many matches");
2292 return source.FirstOrDefault ();
2295 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2298 .Where (m => m.Name == method)
2299 .EnsureZeroOrOne ();
2302 public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
2304 TypeDefinition def = type as TypeDefinition;
2306 return new MemberReference [0];
2307 CustomAttribute defMemberAttr = def.CustomAttributes
2308 .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
2309 if (defMemberAttr == null)
2310 return new MemberReference [0];
2311 string name = (string) defMemberAttr.ConstructorArguments [0].Value;
2312 return def.Properties
2313 .Where (p => p.Name == name)
2314 .Select (p => (MemberReference) p);
2317 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2319 return assembly.Modules.SelectMany (md => md.GetAllTypes ());
2322 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2324 return GetTypes (assembly)
2325 .Where (td => td.FullName == type)
2326 .EnsureZeroOrOne ();
2329 public static bool IsGenericType (this TypeReference type)
2331 return type.GenericParameters.Count > 0;
2334 public static bool IsGenericMethod (this MethodReference method)
2336 return method.GenericParameters.Count > 0;
2339 public static MemberReference Resolve (this MemberReference member)
2341 FieldReference fr = member as FieldReference;
2343 return fr.Resolve ();
2344 MethodReference mr = member as MethodReference;
2346 return mr.Resolve ();
2347 TypeReference tr = member as TypeReference;
2349 return tr.Resolve ();
2350 PropertyReference pr = member as PropertyReference;
2353 EventReference er = member as EventReference;
2356 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2359 public static TypeReference GetUnderlyingType (this TypeDefinition type)
2363 return type.Fields.First (f => f.Name == "value__").FieldType;
2366 public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
2368 return self.Types.SelectMany (t => t.GetAllTypes ());
2371 static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
2375 if (!self.HasNestedTypes)
2378 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
2383 static class DocUtils {
2384 public static bool IsExplicitlyImplemented (MethodDefinition method)
2386 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2389 public static string GetTypeDotMember (string name)
2391 int startType, startMethod;
2392 startType = startMethod = -1;
2393 for (int i = 0; i < name.Length; ++i) {
2394 if (name [i] == '.') {
2395 startType = startMethod;
2399 return name.Substring (startType+1);
2402 public static string GetMember (string name)
2404 int i = name.LastIndexOf ('.');
2407 return name.Substring (i+1);
2410 public static void GetInfoForExplicitlyImplementedMethod (
2411 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2415 if (method.Overrides.Count != 1)
2416 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2417 iface = method.Overrides [0].DeclaringType;
2418 ifaceMethod = method.Overrides [0];
2421 public static string GetPropertyName (PropertyDefinition pi)
2423 // Issue: (g)mcs-generated assemblies that explicitly implement
2424 // properties don't specify the full namespace, just the
2425 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2426 MethodDefinition method = pi.GetMethod;
2428 method = pi.SetMethod;
2429 if (!IsExplicitlyImplemented (method))
2432 // Need to determine appropriate namespace for this member.
2433 TypeReference iface;
2434 MethodReference ifaceMethod;
2435 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2436 return string.Join (".", new string[]{
2437 DocTypeFullMemberFormatter.Default.GetName (iface),
2438 GetMember (pi.Name)});
2441 public static string GetNamespace (TypeReference type)
2443 if (type.GetElementType ().IsNested)
2444 type = type.GetElementType ();
2445 while (type != null && type.IsNested)
2446 type = type.DeclaringType;
2448 return string.Empty;
2449 return type.Namespace;
2452 public static string PathCombine (string dir, string path)
2458 return Path.Combine (dir, path);
2461 public static bool IsExtensionMethod (MethodDefinition method)
2464 method.CustomAttributes
2465 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2466 && method.DeclaringType.CustomAttributes
2467 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
2470 public static bool IsDelegate (TypeDefinition type)
2472 TypeReference baseRef = type.BaseType;
2473 if (baseRef == null)
2475 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2476 baseRef.FullName == "System.MulticastDelegate";
2479 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2481 List<TypeReference> decls = new List<TypeReference> ();
2483 while (type.DeclaringType != null) {
2484 decls.Add (type.DeclaringType);
2485 type = type.DeclaringType;
2491 public static int GetGenericArgumentCount (TypeReference type)
2493 GenericInstanceType inst = type as GenericInstanceType;
2495 ? inst.GenericArguments.Count
2496 : type.GenericParameters.Count;
2499 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
2501 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
2502 List<TypeReference> userInterfaces = new List<TypeReference> ();
2503 foreach (TypeReference iface in type.Interfaces) {
2504 TypeReference lookup = iface.Resolve () ?? iface;
2505 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
2506 userInterfaces.Add (iface);
2508 return userInterfaces;
2511 private static string GetQualifiedTypeName (TypeReference type)
2513 return "[" + type.Scope.Name + "]" + type.FullName;
2516 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
2518 HashSet<string> inheritedInterfaces = new HashSet<string> ();
2519 Action<TypeDefinition> a = null;
2521 if (t == null) return;
2522 foreach (TypeReference r in t.Interfaces) {
2523 inheritedInterfaces.Add (GetQualifiedTypeName (r));
2527 TypeReference baseRef = type.BaseType;
2528 while (baseRef != null) {
2529 TypeDefinition baseDef = baseRef.Resolve ();
2530 if (baseDef != null) {
2532 baseRef = baseDef.BaseType;
2537 foreach (TypeReference r in type.Interfaces)
2539 return inheritedInterfaces;
2543 class DocsNodeInfo {
2544 public DocsNodeInfo (XmlElement node)
2549 public DocsNodeInfo (XmlElement node, TypeDefinition type)
2555 public DocsNodeInfo (XmlElement node, MemberReference member)
2558 SetMemberInfo (member);
2561 void SetType (TypeDefinition type)
2564 throw new ArgumentNullException ("type");
2566 GenericParameters = new List<GenericParameter> (type.GenericParameters);
2567 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
2568 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
2569 for (int i = 0; i < declTypes.Count - 1; ++i) {
2570 int remove = System.Math.Min (maxGenArgs,
2571 DocUtils.GetGenericArgumentCount (declTypes [i]));
2572 maxGenArgs -= remove;
2573 while (remove-- > 0)
2574 GenericParameters.RemoveAt (0);
2576 if (DocUtils.IsDelegate (type)) {
2577 Parameters = type.GetMethod("Invoke").Parameters;
2578 ReturnType = type.GetMethod("Invoke").ReturnType;
2579 ReturnIsReturn = true;
2583 void SetMemberInfo (MemberReference member)
2586 throw new ArgumentNullException ("member");
2587 ReturnIsReturn = true;
2591 if (member is MethodReference ) {
2592 MethodReference mr = (MethodReference) member;
2593 Parameters = mr.Parameters;
2594 if (mr.IsGenericMethod ()) {
2595 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
2598 else if (member is PropertyDefinition) {
2599 Parameters = ((PropertyDefinition) member).Parameters;
2602 if (member is MethodDefinition) {
2603 ReturnType = ((MethodDefinition) member).ReturnType;
2604 } else if (member is PropertyDefinition) {
2605 ReturnType = ((PropertyDefinition) member).PropertyType;
2606 ReturnIsReturn = false;
2609 // no remarks section for enum members
2610 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
2614 public TypeReference ReturnType;
2615 public List<GenericParameter> GenericParameters;
2616 public IList<ParameterDefinition> Parameters;
2617 public bool ReturnIsReturn;
2618 public XmlElement Node;
2619 public bool AddRemarks = true;
2620 public MemberReference Member;
2621 public TypeDefinition Type;
2624 class DocumentationEnumerator {
2626 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2628 return GetDocumentationTypes (assembly, forTypes, null);
2631 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2633 foreach (TypeDefinition type in assembly.GetTypes()) {
2634 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
2636 if (seen != null && seen.Contains (type.FullName))
2639 foreach (TypeDefinition nested in type.NestedTypes)
2640 yield return nested;
2644 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2646 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
2647 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
2648 oldmember.RemoveAttribute ("__monodocer-seen__");
2651 MemberReference m = GetMember (type, new DocumentationMember (oldmember));
2653 yield return new DocsNodeInfo (oldmember);
2656 yield return new DocsNodeInfo (oldmember, m);
2661 protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
2663 string membertype = member.MemberType;
2665 string returntype = member.ReturnType;
2667 string docName = member.MemberName;
2668 string[] docTypeParams = GetTypeParameters (docName);
2670 // Loop through all members in this type with the same name
2671 foreach (MemberReference mi in GetReflectionMembers (type, docName)) {
2672 if (mi is TypeDefinition) continue;
2673 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
2675 if (MDocUpdater.IsPrivate (mi))
2678 IList<ParameterDefinition> pis = null;
2679 string[] typeParams = null;
2680 if (mi is MethodDefinition) {
2681 MethodDefinition mb = (MethodDefinition) mi;
2682 pis = mb.Parameters;
2683 if (docTypeParams != null && mb.IsGenericMethod ()) {
2684 IList<GenericParameter> args = mb.GenericParameters;
2685 if (args.Count == docTypeParams.Length) {
2686 typeParams = args.Select (p => p.Name).ToArray ();
2690 else if (mi is PropertyDefinition)
2691 pis = ((PropertyDefinition)mi).Parameters;
2693 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
2694 int pcount = pis == null ? 0 : pis.Count;
2695 if (mcount != pcount)
2698 MethodDefinition mDef = mi as MethodDefinition;
2699 if (mDef != null && !mDef.IsConstructor) {
2700 // Casting operators can overload based on return type.
2701 if (returntype != GetReplacedString (
2702 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
2703 typeParams, docTypeParams)) {
2711 for (int i = 0; i < pis.Count; i++) {
2712 string paramType = GetReplacedString (
2713 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
2714 typeParams, docTypeParams);
2715 if (paramType != (string) member.Parameters [i]) {
2720 if (!good) continue;
2728 static string[] GetTypeParameters (string docName)
2730 if (docName [docName.Length-1] != '>')
2732 StringList types = new StringList ();
2733 int endToken = docName.Length-2;
2734 int i = docName.Length-2;
2736 if (docName [i] == ',' || docName [i] == '<') {
2737 types.Add (docName.Substring (i + 1, endToken - i));
2740 if (docName [i] == '<')
2745 return types.ToArray ();
2748 protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
2750 // need to worry about 4 forms of //@MemberName values:
2751 // 1. "Normal" (non-generic) member names: GetEnumerator
2753 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
2754 // - try as-is, and try type.member (due to "kludge" for property
2756 // 3. "Normal" Generic member names: Sort<T> (CSC)
2757 // - need to remove generic parameters --> "Sort"
2758 // 4. Explicitly-implemented interface members for generic interfaces:
2759 // -- System.Collections.Generic.IEnumerable<T>.Current
2760 // - Try as-is, and try type.member, *keeping* the generic parameters.
2761 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
2762 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
2763 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
2764 // this as (1) or (2).
2765 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
2767 foreach (MemberReference mi in type.GetMembers (docName))
2769 if (CountChars (docName, '.') > 0)
2770 // might be a property; try only type.member instead of
2771 // namespace.type.member.
2772 foreach (MemberReference mi in
2773 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
2780 int startLt, startType, startMethod;
2781 startLt = startType = startMethod = -1;
2782 for (int i = 0; i < docName.Length; ++i) {
2783 switch (docName [i]) {
2792 if (numLt == 0 && (i + 1) < docName.Length)
2793 // there's another character in docName, so this <...> sequence is
2794 // probably part of a generic type -- case 4.
2798 startType = startMethod;
2804 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
2806 foreach (MemberReference mi in type.GetMembers (refName))
2810 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
2813 // If we _still_ haven't found it, we've hit another generic naming issue:
2814 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
2815 // explicitly-implemented METHOD names (not properties), e.g.
2816 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
2817 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
2818 // which the XML docs will contain.
2820 // Alas, we can't derive the Mono name from docName, so we need to iterate
2821 // over all member names, convert them into CSC format, and compare... :-(
2824 foreach (MemberReference mi in type.GetMembers ()) {
2825 if (MDocUpdater.GetMemberName (mi) == docName)
2830 static string GetReplacedString (string typeName, string[] from, string[] to)
2834 for (int i = 0; i < from.Length; ++i)
2835 typeName = typeName.Replace (from [i], to [i]);
2839 private static int CountChars (string s, char c)
2842 for (int i = 0; i < s.Length; ++i) {
2850 class EcmaDocumentationEnumerator : DocumentationEnumerator {
2855 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
2858 this.ecmadocs = ecmaDocs;
2861 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2863 HashSet<string> seen = new HashSet<string> ();
2864 return GetDocumentationTypes (assembly, forTypes, seen)
2865 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
2868 IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2871 while (ecmadocs.Read ()) {
2872 switch (ecmadocs.Name) {
2874 if (typeDepth == -1)
2875 typeDepth = ecmadocs.Depth;
2876 if (ecmadocs.NodeType != XmlNodeType.Element)
2878 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
2880 string typename = ecmadocs.GetAttribute ("FullName");
2881 string typename2 = MDocUpdater.GetTypeFileName (typename);
2882 if (forTypes != null &&
2883 forTypes.BinarySearch (typename) < 0 &&
2884 typename != typename2 &&
2885 forTypes.BinarySearch (typename2) < 0)
2888 if ((t = assembly.GetType (typename)) == null &&
2889 (t = assembly.GetType (typename2)) == null)
2891 seen.Add (typename);
2892 if (typename != typename2)
2893 seen.Add (typename2);
2894 Console.WriteLine (" Import: {0}", t.FullName);
2895 if (ecmadocs.Name != "Docs") {
2896 int depth = ecmadocs.Depth;
2897 while (ecmadocs.Read ()) {
2898 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
2902 if (!ecmadocs.IsStartElement ("Docs"))
2903 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
2913 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2915 return GetMembers (basefile, type)
2916 .Concat (base.GetDocumentationMembers (basefile, type));
2919 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
2921 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
2924 if (ecmadocs.IsEmptyElement)
2927 int membersDepth = ecmadocs.Depth;
2929 while (go && ecmadocs.Read ()) {
2930 switch (ecmadocs.Name) {
2932 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
2934 DocumentationMember dm = new DocumentationMember (ecmadocs);
2935 string xp = MDocUpdater.GetXPathForMember (dm);
2936 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
2938 if (oldmember == null) {
2939 m = GetMember (type, dm);
2941 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2942 type.FullName, dm.MemberSignatures ["C#"]);
2943 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
2946 // oldmember lookup may have failed due to type parameter renames.
2948 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
2949 if (oldmember == null) {
2950 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
2951 oldmember = basefile.CreateElement ("Member");
2952 oldmember.SetAttribute ("MemberName", dm.MemberName);
2953 members.AppendChild (oldmember);
2954 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
2955 XmlElement ms = basefile.CreateElement ("MemberSignature");
2956 ms.SetAttribute ("Language", key);
2957 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
2958 oldmember.AppendChild (ms);
2960 oldmember.SetAttribute ("__monodocer-seen__", "true");
2961 Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
2966 m = GetMember (type, new DocumentationMember (oldmember));
2968 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2969 type.FullName, dm.MemberSignatures ["C#"]);
2972 oldmember.SetAttribute ("__monodocer-seen__", "true");
2974 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
2975 if (ecmadocs.Name != "Docs")
2976 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
2981 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
2990 abstract class DocumentationImporter {
2992 public abstract void ImportDocumentation (DocsNodeInfo info);
2995 class MsxdocDocumentationImporter : DocumentationImporter {
2997 XmlDocument slashdocs;
2999 public MsxdocDocumentationImporter (string file)
3001 var xml = File.ReadAllText (file);
3003 // Ensure Unix line endings
3004 xml = xml.Replace ("\r", "");
3006 slashdocs = new XmlDocument();
3007 slashdocs.LoadXml (xml);
3010 public override void ImportDocumentation (DocsNodeInfo info)
3012 XmlNode elem = GetDocs (info.Member ?? info.Type);
3017 XmlElement e = info.Node;
3019 if (elem.SelectSingleNode("summary") != null)
3020 MDocUpdater.ClearElement(e, "summary");
3021 if (elem.SelectSingleNode("remarks") != null)
3022 MDocUpdater.ClearElement(e, "remarks");
3023 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3024 MDocUpdater.ClearElement(e, "value");
3025 MDocUpdater.ClearElement(e, "returns");
3028 foreach (XmlNode child in elem.ChildNodes) {
3029 switch (child.Name) {
3032 XmlAttribute name = child.Attributes ["name"];
3035 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3037 p2.InnerXml = child.InnerXml;
3040 // Occasionally XML documentation will use <returns/> on
3041 // properties, so let's try to normalize things.
3044 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3045 v.InnerXml = child.InnerXml;
3051 case "permission": {
3052 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3055 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3057 a = e.OwnerDocument.CreateElement (child.Name);
3058 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
3061 a.InnerXml = child.InnerXml;
3065 XmlAttribute cref = child.Attributes ["cref"];
3068 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3070 a = e.OwnerDocument.CreateElement ("altmember");
3071 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
3078 if (child.NodeType == XmlNodeType.Element &&
3079 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3082 MDocUpdater.CopyNode (child, e);
3089 private XmlNode GetDocs (MemberReference member)
3091 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3092 if (slashdocsig != null)
3093 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3098 class EcmaDocumentationImporter : DocumentationImporter {
3102 public EcmaDocumentationImporter (XmlReader ecmaDocs)
3104 this.ecmadocs = ecmaDocs;
3107 public override void ImportDocumentation (DocsNodeInfo info)
3109 if (!ecmadocs.IsStartElement ("Docs")) {
3113 XmlElement e = info.Node;
3115 int depth = ecmadocs.Depth;
3116 ecmadocs.ReadStartElement ("Docs");
3117 while (ecmadocs.Read ()) {
3118 if (ecmadocs.Name == "Docs") {
3119 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3122 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3124 if (!ecmadocs.IsStartElement ())
3126 switch (ecmadocs.Name) {
3129 string name = ecmadocs.GetAttribute ("name");
3132 XmlNode doc = e.SelectSingleNode (
3133 ecmadocs.Name + "[@name='" + name + "']");
3134 string value = ecmadocs.ReadInnerXml ();
3136 doc.InnerXml = value.Replace ("\r", "");
3143 string name = ecmadocs.Name;
3144 string cref = ecmadocs.GetAttribute ("cref");
3147 XmlNode doc = e.SelectSingleNode (
3148 ecmadocs.Name + "[@cref='" + cref + "']");
3149 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3151 doc.InnerXml = value;
3153 XmlElement n = e.OwnerDocument.CreateElement (name);
3154 n.SetAttribute ("cref", cref);
3161 string name = ecmadocs.Name;
3162 string xpath = ecmadocs.Name;
3163 StringList attributes = new StringList (ecmadocs.AttributeCount);
3164 if (ecmadocs.MoveToFirstAttribute ()) {
3166 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3167 } while (ecmadocs.MoveToNextAttribute ());
3168 ecmadocs.MoveToContent ();
3170 if (attributes.Count > 0) {
3171 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3173 XmlNode doc = e.SelectSingleNode (xpath);
3174 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3176 doc.InnerXml = value;
3179 XmlElement n = e.OwnerDocument.CreateElement (name);
3181 foreach (string a in attributes) {
3182 int eq = a.IndexOf ('=');
3183 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3194 class DocumentationMember {
3195 public StringToStringMap MemberSignatures = new StringToStringMap ();
3196 public string ReturnType;
3197 public StringList Parameters;
3198 public string MemberName;
3199 public string MemberType;
3201 public DocumentationMember (XmlReader reader)
3203 MemberName = reader.GetAttribute ("MemberName");
3204 int depth = reader.Depth;
3206 StringList p = new StringList ();
3208 if (reader.NodeType != XmlNodeType.Element)
3210 switch (reader.Name) {
3211 case "MemberSignature":
3212 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3215 MemberType = reader.ReadElementString ();
3218 if (reader.Depth == depth + 2)
3219 ReturnType = reader.ReadElementString ();
3222 if (reader.Depth == depth + 2)
3223 p.Add (reader.GetAttribute ("Type"));
3226 if (reader.Depth == depth + 1)
3230 } while (go && reader.Read () && reader.Depth >= depth);
3236 public DocumentationMember (XmlNode node)
3238 MemberName = node.Attributes ["MemberName"].Value;
3239 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3240 XmlAttribute l = n.Attributes ["Language"];
3241 XmlAttribute v = n.Attributes ["Value"];
3242 if (l != null && v != null)
3243 MemberSignatures [l.Value] = v.Value;
3245 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3246 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
3248 ReturnType = rt.InnerText;
3249 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
3251 Parameters = new StringList (p.Count);
3252 for (int i = 0; i < p.Count; ++i)
3253 Parameters.Add (p [i].Attributes ["Type"].Value);
3258 public enum MemberFormatterState {
3261 WithinGenericTypeParameters,
3264 public abstract class MemberFormatter {
3266 public virtual string Language {
3270 public virtual string GetName (MemberReference member)
3272 TypeReference type = member as TypeReference;
3274 return GetTypeName (type);
3275 MethodReference method = member as MethodReference;
3276 if (method != null && method.Name == ".ctor") // method.IsConstructor
3277 return GetConstructorName (method);
3279 return GetMethodName (method);
3280 PropertyReference prop = member as PropertyReference;
3282 return GetPropertyName (prop);
3283 FieldReference field = member as FieldReference;
3285 return GetFieldName (field);
3286 EventReference e = member as EventReference;
3288 return GetEventName (e);
3289 throw new NotSupportedException ("Can't handle: " +
3290 (member == null ? "null" : member.GetType().ToString()));
3293 protected virtual string GetTypeName (TypeReference type)
3296 throw new ArgumentNullException ("type");
3297 return _AppendTypeName (new StringBuilder (type.Name.Length), type).ToString ();
3300 protected virtual char[] ArrayDelimeters {
3301 get {return new char[]{'[', ']'};}
3304 protected virtual MemberFormatterState MemberFormatterState { get; set; }
3306 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type)
3308 if (type is ArrayType) {
3309 TypeSpecification spec = type as TypeSpecification;
3310 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType ())
3311 .Append (ArrayDelimeters [0]);
3312 var origState = MemberFormatterState;
3313 MemberFormatterState = MemberFormatterState.WithinArray;
3314 ArrayType array = (ArrayType) type;
3315 int rank = array.Rank;
3317 buf.Append (new string (',', rank-1));
3318 MemberFormatterState = origState;
3319 return buf.Append (ArrayDelimeters [1]);
3321 if (type is ByReferenceType) {
3322 return AppendRefTypeName (buf, type);
3324 if (type is PointerType) {
3325 return AppendPointerTypeName (buf, type);
3327 AppendNamespace (buf, type);
3328 if (type is GenericParameter) {
3329 return AppendTypeName (buf, type);
3331 GenericInstanceType genInst = type as GenericInstanceType;
3332 if (type.GenericParameters.Count == 0 &&
3333 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3334 return AppendFullTypeName (buf, type);
3336 return AppendGenericType (buf, type);
3339 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3341 string ns = DocUtils.GetNamespace (type);
3342 if (ns != null && ns.Length > 0)
3343 buf.Append (ns).Append ('.');
3347 protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type)
3349 if (type.DeclaringType != null)
3350 AppendFullTypeName (buf, type.DeclaringType).Append (NestedTypeSeparator);
3351 return AppendTypeName (buf, type);
3354 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3356 return AppendTypeName (buf, type.Name);
3359 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3361 int n = typename.IndexOf ("`");
3363 return buf.Append (typename.Substring (0, n));
3364 return buf.Append (typename);
3367 protected virtual string RefTypeModifier {
3371 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type)
3373 TypeSpecification spec = type as TypeSpecification;
3374 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType ())
3375 .Append (RefTypeModifier);
3378 protected virtual string PointerModifier {
3382 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type)
3384 TypeSpecification spec = type as TypeSpecification;
3385 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType ())
3386 .Append (PointerModifier);
3389 protected virtual char[] GenericTypeContainer {
3390 get {return new char[]{'<', '>'};}
3393 protected virtual char NestedTypeSeparator {
3397 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3399 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3400 type is GenericInstanceType ? type.GetElementType () : type);
3401 List<TypeReference> genArgs = GetGenericArguments (type);
3404 bool insertNested = false;
3405 foreach (var decl in decls) {
3406 TypeReference declDef = decl.Resolve () ?? decl;
3408 buf.Append (NestedTypeSeparator);
3410 insertNested = true;
3411 AppendTypeName (buf, declDef);
3412 int ac = DocUtils.GetGenericArgumentCount (declDef);
3416 buf.Append (GenericTypeContainer [0]);
3417 var origState = MemberFormatterState;
3418 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3419 _AppendTypeName (buf, genArgs [argIdx++]);
3420 for (int i = 1; i < c; ++i)
3421 _AppendTypeName (buf.Append (","), genArgs [argIdx++]);
3422 MemberFormatterState = origState;
3423 buf.Append (GenericTypeContainer [1]);
3429 protected List<TypeReference> GetGenericArguments (TypeReference type)
3431 var args = new List<TypeReference> ();
3432 GenericInstanceType inst = type as GenericInstanceType;
3434 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
3436 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
3440 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3445 protected virtual string GetConstructorName (MethodReference constructor)
3447 return constructor.Name;
3450 protected virtual string GetMethodName (MethodReference method)
3455 protected virtual string GetPropertyName (PropertyReference property)
3457 return property.Name;
3460 protected virtual string GetFieldName (FieldReference field)
3465 protected virtual string GetEventName (EventReference e)
3470 public virtual string GetDeclaration (MemberReference member)
3473 throw new ArgumentNullException ("member");
3474 TypeDefinition type = member as TypeDefinition;
3476 return GetTypeDeclaration (type);
3477 MethodDefinition method = member as MethodDefinition;
3478 if (method != null && method.IsConstructor)
3479 return GetConstructorDeclaration (method);
3481 return GetMethodDeclaration (method);
3482 PropertyDefinition prop = member as PropertyDefinition;
3484 return GetPropertyDeclaration (prop);
3485 FieldDefinition field = member as FieldDefinition;
3487 return GetFieldDeclaration (field);
3488 EventDefinition e = member as EventDefinition;
3490 return GetEventDeclaration (e);
3491 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3494 protected virtual string GetTypeDeclaration (TypeDefinition type)
3497 throw new ArgumentNullException ("type");
3498 StringBuilder buf = new StringBuilder (type.Name.Length);
3499 _AppendTypeName (buf, type);
3500 AppendGenericTypeConstraints (buf, type);
3501 return buf.ToString ();
3504 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
3506 return GetConstructorName (constructor);
3509 protected virtual string GetMethodDeclaration (MethodDefinition method)
3511 // Special signature for destructors.
3512 if (method.Name == "Finalize" && method.Parameters.Count == 0)
3513 return GetFinalizerName (method);
3515 StringBuilder buf = new StringBuilder ();
3517 AppendVisibility (buf, method);
3518 if (buf.Length == 0 &&
3519 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3522 AppendModifiers (buf, method);
3524 if (buf.Length != 0)
3526 buf.Append (GetTypeName (method.MethodReturnType)).Append (" ");
3528 AppendMethodName (buf, method);
3529 AppendGenericMethod (buf, method).Append (" ");
3530 AppendParameters (buf, method, method.Parameters);
3531 AppendGenericMethodConstraints (buf, method);
3532 return buf.ToString ();
3535 protected virtual string GetTypeName (MethodReturnType returnType)
3537 return GetName (returnType.ReturnType);
3540 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3542 return buf.Append (method.Name);
3545 protected virtual string GetFinalizerName (MethodDefinition method)
3550 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3555 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3560 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3565 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
3570 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3575 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
3577 return GetPropertyName (property);
3580 protected virtual string GetFieldDeclaration (FieldDefinition field)
3582 return GetFieldName (field);
3585 protected virtual string GetEventDeclaration (EventDefinition e)
3587 return GetEventName (e);
3591 class ILFullMemberFormatter : MemberFormatter {
3593 public override string Language {
3594 get {return "ILAsm";}
3597 protected override char NestedTypeSeparator {
3603 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3605 if (GetBuiltinType (type.FullName) != null)
3607 string ns = DocUtils.GetNamespace (type);
3608 if (ns != null && ns.Length > 0) {
3609 if (type.IsValueType)
3610 buf.Append ("valuetype ");
3612 buf.Append ("class ");
3613 buf.Append (ns).Append ('.');
3618 private static string GetBuiltinType (string t)
3621 case "System.Byte": return "unsigned int8";
3622 case "System.SByte": return "int8";
3623 case "System.Int16": return "int16";
3624 case "System.Int32": return "int32";
3625 case "System.Int64": return "int64";
3626 case "System.IntPtr": return "native int";
3628 case "System.UInt16": return "unsigned int16";
3629 case "System.UInt32": return "unsigned int32";
3630 case "System.UInt64": return "unsigned int64";
3631 case "System.UIntPtr": return "native unsigned int";
3633 case "System.Single": return "float32";
3634 case "System.Double": return "float64";
3635 case "System.Boolean": return "bool";
3636 case "System.Char": return "char";
3637 case "System.Void": return "void";
3638 case "System.String": return "string";
3639 case "System.Object": return "object";
3644 protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
3646 return buf.Append (typename);
3649 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3651 var gp = type as GenericParameter;
3652 if (type is GenericParameter)
3653 return AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
3655 string s = GetBuiltinType (type.FullName);
3657 return buf.Append (s);
3658 return base.AppendTypeName (buf, type);
3661 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
3663 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
3664 return buf.Append (type.Owner is TypeReference ? "!" : "!!");
3666 GenericParameterAttributes attrs = type.Attributes;
3667 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
3668 buf.Append ("class ");
3669 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
3670 buf.Append ("struct ");
3671 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
3672 buf.Append (".ctor ");
3673 IList<TypeReference> constraints = type.Constraints;
3674 MemberFormatterState = 0;
3675 if (constraints.Count > 0) {
3676 var full = new ILFullMemberFormatter ();
3677 buf.Append ("(").Append (full.GetName (constraints [0]));
3678 for (int i = 1; i < constraints.Count; ++i) {
3679 buf.Append (", ").Append (full.GetName (constraints [i]));
3683 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3685 if ((attrs & GenericParameterAttributes.Covariant) != 0)
3687 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
3692 protected override string GetTypeDeclaration (TypeDefinition type)
3694 string visibility = GetTypeVisibility (type.Attributes);
3695 if (visibility == null)
3698 StringBuilder buf = new StringBuilder ();
3700 buf.Append (".class ");
3702 buf.Append ("nested ");
3703 buf.Append (visibility).Append (" ");
3704 if (type.IsInterface)
3705 buf.Append ("interface ");
3706 if (type.IsSequentialLayout)
3707 buf.Append ("sequential ");
3708 if (type.IsAutoLayout)
3709 buf.Append ("auto ");
3710 if (type.IsAnsiClass)
3711 buf.Append ("ansi ");
3712 if (type.IsAbstract)
3713 buf.Append ("abstract ");
3714 if (type.IsSerializable)
3715 buf.Append ("serializable ");
3717 buf.Append ("sealed ");
3718 if (type.IsBeforeFieldInit)
3719 buf.Append ("beforefieldinit ");
3720 var state = MemberFormatterState;
3721 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3722 buf.Append (GetName (type));
3723 MemberFormatterState = state;
3724 var full = new ILFullMemberFormatter ();
3725 if (type.BaseType != null) {
3726 buf.Append (" extends ");
3727 if (type.BaseType.FullName == "System.Object")
3728 buf.Append ("System.Object");
3730 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
3733 foreach (var name in type.Interfaces
3734 .Select (i => full.GetName (i))
3735 .OrderBy (n => n)) {
3737 buf.Append (" implements ");
3746 return buf.ToString ();
3749 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3751 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3752 type is GenericInstanceType ? type.GetElementType () : type);
3754 foreach (var decl in decls) {
3755 TypeReference declDef = decl.Resolve () ?? decl;
3757 buf.Append (NestedTypeSeparator);
3760 AppendTypeName (buf, declDef);
3764 foreach (TypeReference arg in GetGenericArguments (type)) {
3768 _AppendTypeName (buf, arg);
3774 static string GetTypeVisibility (TypeAttributes ta)
3776 switch (ta & TypeAttributes.VisibilityMask) {
3777 case TypeAttributes.Public:
3778 case TypeAttributes.NestedPublic:
3781 case TypeAttributes.NestedFamily:
3782 case TypeAttributes.NestedFamORAssem:
3790 protected override string GetConstructorDeclaration (MethodDefinition constructor)
3792 return GetMethodDeclaration (constructor);
3795 protected override string GetMethodDeclaration (MethodDefinition method)
3797 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
3800 var buf = new StringBuilder ();
3801 buf.Append (".method ");
3802 AppendVisibility (buf, method);
3803 if (method.IsStatic)
3804 buf.Append ("static ");
3805 if (method.IsHideBySig)
3806 buf.Append ("hidebysig ");
3807 if (method.IsPInvokeImpl) {
3808 var info = method.PInvokeInfo;
3809 buf.Append ("pinvokeimpl (\"")
3810 .Append (info.Module.Name)
3811 .Append ("\" as \"")
3812 .Append (info.EntryPoint)
3814 if (info.IsCharSetAuto)
3815 buf.Append (" auto");
3816 if (info.IsCharSetUnicode)
3817 buf.Append (" unicode");
3818 if (info.IsCharSetAnsi)
3819 buf.Append (" ansi");
3820 if (info.IsCallConvCdecl)
3821 buf.Append (" cdecl");
3822 if (info.IsCallConvStdCall)
3823 buf.Append (" stdcall");
3824 if (info.IsCallConvWinapi)
3825 buf.Append (" winapi");
3826 if (info.IsCallConvThiscall)
3827 buf.Append (" thiscall");
3828 if (info.SupportsLastError)
3829 buf.Append (" lasterr");
3832 if (method.IsSpecialName)
3833 buf.Append ("specialname ");
3834 if (method.IsRuntimeSpecialName)
3835 buf.Append ("rtspecialname ");
3836 if (method.IsNewSlot)
3837 buf.Append ("newslot ");
3838 if (method.IsVirtual)
3839 buf.Append ("virtual ");
3840 if (!method.IsStatic)
3841 buf.Append ("instance ");
3842 _AppendTypeName (buf, method.ReturnType);
3844 .Append (method.Name);
3845 if (method.IsGenericMethod ()) {
3846 var state = MemberFormatterState;
3847 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3848 IList<GenericParameter> args = method.GenericParameters;
3849 if (args.Count > 0) {
3851 _AppendTypeName (buf, args [0]);
3852 for (int i = 1; i < args.Count; ++i)
3853 _AppendTypeName (buf.Append (", "), args [i]);
3856 MemberFormatterState = state;
3861 for (int i = 0; i < method.Parameters.Count; ++i) {
3865 _AppendTypeName (buf, method.Parameters [i].ParameterType);
3867 buf.Append (method.Parameters [i].Name);
3871 buf.Append (" cil");
3872 if (method.IsRuntime)
3873 buf.Append (" runtime");
3874 if (method.IsManaged)
3875 buf.Append (" managed");
3877 return buf.ToString ();
3880 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3882 if (DocUtils.IsExplicitlyImplemented (method)) {
3883 TypeReference iface;
3884 MethodReference ifaceMethod;
3885 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3886 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3888 .Append (ifaceMethod.Name);
3890 return base.AppendMethodName (buf, method);
3893 protected override string RefTypeModifier {
3897 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3899 if (method.IsPublic)
3900 return buf.Append ("public ");
3901 if (method.IsFamilyAndAssembly)
3902 return buf.Append ("familyandassembly");
3903 if (method.IsFamilyOrAssembly)
3904 return buf.Append ("familyorassembly");
3905 if (method.IsFamily)
3906 return buf.Append ("family");
3910 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3912 string modifiers = String.Empty;
3913 if (method.IsStatic) modifiers += " static";
3914 if (method.IsVirtual && !method.IsAbstract) {
3915 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3916 else modifiers += " override";
3918 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
3919 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
3920 if (method.IsFinal) modifiers += " sealed";
3921 if (modifiers == " virtual sealed") modifiers = "";
3923 return buf.Append (modifiers);
3926 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3928 if (method.IsGenericMethod ()) {
3929 IList<GenericParameter> args = method.GenericParameters;
3930 if (args.Count > 0) {
3932 buf.Append (args [0].Name);
3933 for (int i = 1; i < args.Count; ++i)
3934 buf.Append (",").Append (args [i].Name);
3941 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
3943 return AppendParameters (buf, method, parameters, '(', ')');
3946 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
3950 if (parameters.Count > 0) {
3951 if (DocUtils.IsExtensionMethod (method))
3952 buf.Append ("this ");
3953 AppendParameter (buf, parameters [0]);
3954 for (int i = 1; i < parameters.Count; ++i) {
3956 AppendParameter (buf, parameters [i]);
3960 return buf.Append (end);
3963 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
3965 if (parameter.ParameterType is ByReferenceType) {
3966 if (parameter.IsOut)
3967 buf.Append ("out ");
3969 buf.Append ("ref ");
3971 buf.Append (GetName (parameter.ParameterType)).Append (" ");
3972 return buf.Append (parameter.Name);
3975 protected override string GetPropertyDeclaration (PropertyDefinition property)
3977 MethodDefinition gm = null, sm = null;
3979 string get_visible = null;
3980 if ((gm = property.GetMethod) != null &&
3981 (DocUtils.IsExplicitlyImplemented (gm) ||
3982 (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
3983 get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
3984 string set_visible = null;
3985 if ((sm = property.SetMethod) != null &&
3986 (DocUtils.IsExplicitlyImplemented (sm) ||
3987 (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
3988 set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
3990 if ((set_visible == null) && (get_visible == null))
3994 StringBuilder buf = new StringBuilder ()
3995 .Append (".property ");
3996 if (!(gm ?? sm).IsStatic)
3997 buf.Append ("instance ");
3998 _AppendTypeName (buf, property.PropertyType);
3999 buf.Append (' ').Append (property.Name);
4000 if (!property.HasParameters || property.Parameters.Count == 0)
4001 return buf.ToString ();
4005 foreach (ParameterDefinition p in property.Parameters) {
4009 _AppendTypeName (buf, p.ParameterType);
4013 return buf.ToString ();
4016 protected override string GetFieldDeclaration (FieldDefinition field)
4018 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4019 if (declType.IsEnum && field.Name == "value__")
4020 return null; // This member of enums aren't documented.
4022 StringBuilder buf = new StringBuilder ();
4023 AppendFieldVisibility (buf, field);
4024 if (buf.Length == 0)
4027 buf.Insert (0, ".field ");
4030 buf.Append ("static ");
4031 if (field.IsInitOnly)
4032 buf.Append ("initonly ");
4033 if (field.IsLiteral)
4034 buf.Append ("literal ");
4035 _AppendTypeName (buf, field.FieldType);
4036 buf.Append (' ').Append (field.Name);
4037 AppendFieldValue (buf, field);
4039 return buf.ToString ();
4042 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4045 return buf.Append ("public ");
4046 if (field.IsFamilyAndAssembly)
4047 return buf.Append ("familyandassembly ");
4048 if (field.IsFamilyOrAssembly)
4049 return buf.Append ("familyorassembly ");
4051 return buf.Append ("family ");
4055 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4057 // enums have a value__ field, which we ignore
4058 if (field.DeclaringType.IsGenericType ())
4060 if (field.HasConstant && field.IsLiteral) {
4063 val = field.Constant;
4068 buf.Append (" = ").Append ("null");
4069 else if (val is Enum)
4071 .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4073 .Append (val.ToString ())
4075 else if (val is IFormattable) {
4076 string value = ((IFormattable)val).ToString();
4079 buf.Append ("\"" + value + "\"");
4081 buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4090 protected override string GetEventDeclaration (EventDefinition e)
4092 StringBuilder buf = new StringBuilder ();
4093 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4098 buf.Append (".event ")
4099 .Append (GetName (e.EventType))
4103 return buf.ToString ();
4107 class ILMemberFormatter : ILFullMemberFormatter {
4108 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4114 class CSharpFullMemberFormatter : MemberFormatter {
4116 public override string Language {
4120 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4122 string ns = DocUtils.GetNamespace (type);
4123 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
4124 buf.Append (ns).Append ('.');
4128 private string GetCSharpType (string t)
4131 case "System.Byte": return "byte";
4132 case "System.SByte": return "sbyte";
4133 case "System.Int16": return "short";
4134 case "System.Int32": return "int";
4135 case "System.Int64": return "long";
4137 case "System.UInt16": return "ushort";
4138 case "System.UInt32": return "uint";
4139 case "System.UInt64": return "ulong";
4141 case "System.Single": return "float";
4142 case "System.Double": return "double";
4143 case "System.Decimal": return "decimal";
4144 case "System.Boolean": return "bool";
4145 case "System.Char": return "char";
4146 case "System.Void": return "void";
4147 case "System.String": return "string";
4148 case "System.Object": return "object";
4153 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
4155 if (type is GenericParameter)
4156 return AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
4157 string t = type.FullName;
4158 if (!t.StartsWith ("System.")) {
4159 return base.AppendTypeName (buf, type);
4162 string s = GetCSharpType (t);
4164 return buf.Append (s);
4166 return base.AppendTypeName (buf, type);
4169 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
4171 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
4173 GenericParameterAttributes attrs = type.Attributes;
4174 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
4175 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
4179 buf.Append ("out ");
4183 protected override string GetTypeDeclaration (TypeDefinition type)
4185 string visibility = GetTypeVisibility (type.Attributes);
4186 if (visibility == null)
4189 StringBuilder buf = new StringBuilder ();
4191 buf.Append (visibility);
4194 MemberFormatter full = new CSharpFullMemberFormatter ();
4196 if (DocUtils.IsDelegate (type)) {
4197 buf.Append("delegate ");
4198 MethodDefinition invoke = type.GetMethod ("Invoke");
4199 buf.Append (full.GetName (invoke.ReturnType)).Append (" ");
4200 buf.Append (GetName (type));
4201 AppendParameters (buf, invoke, invoke.Parameters);
4202 AppendGenericTypeConstraints (buf, type);
4205 return buf.ToString();
4208 if (type.IsAbstract && !type.IsInterface)
4209 buf.Append("abstract ");
4210 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
4211 buf.Append("sealed ");
4212 buf.Replace ("abstract sealed", "static");
4214 buf.Append (GetTypeKind (type));
4216 buf.Append (GetCSharpType (type.FullName) == null
4221 TypeReference basetype = type.BaseType;
4222 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
4225 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
4226 .Select (iface => full.GetName (iface))
4230 if (basetype != null || interface_names.Count > 0)
4233 if (basetype != null) {
4234 buf.Append (full.GetName (basetype));
4235 if (interface_names.Count > 0)
4239 for (int i = 0; i < interface_names.Count; i++){
4242 buf.Append (interface_names [i]);
4244 AppendGenericTypeConstraints (buf, type);
4247 return buf.ToString ();
4250 static string GetTypeKind (TypeDefinition t)
4256 if (t.IsClass || t.FullName == "System.Enum")
4260 throw new ArgumentException(t.FullName);
4263 static string GetTypeVisibility (TypeAttributes ta)
4265 switch (ta & TypeAttributes.VisibilityMask) {
4266 case TypeAttributes.Public:
4267 case TypeAttributes.NestedPublic:
4270 case TypeAttributes.NestedFamily:
4271 case TypeAttributes.NestedFamORAssem:
4279 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4281 if (type.GenericParameters.Count == 0)
4283 return AppendConstraints (buf, type.GenericParameters);
4286 private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
4288 foreach (GenericParameter genArg in genArgs) {
4289 GenericParameterAttributes attrs = genArg.Attributes;
4290 IList<TypeReference> constraints = genArg.Constraints;
4291 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
4294 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
4295 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
4296 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
4299 if (!isref && !isvt && !isnew && constraints.Count == 0)
4301 buf.Append (" where ").Append (genArg.Name).Append (" : ");
4303 buf.Append ("class");
4307 buf.Append ("struct");
4310 if (constraints.Count > 0 && !isvt) {
4313 buf.Append (GetTypeName (constraints [0]));
4314 for (int i = 1; i < constraints.Count; ++i)
4315 buf.Append (", ").Append (GetTypeName (constraints [i]));
4317 if (isnew && !isvt) {
4320 buf.Append ("new()");
4326 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4328 StringBuilder buf = new StringBuilder ();
4329 AppendVisibility (buf, constructor);
4330 if (buf.Length == 0)
4334 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
4335 AppendParameters (buf, constructor, constructor.Parameters);
4338 return buf.ToString ();
4341 protected override string GetMethodDeclaration (MethodDefinition method)
4343 string decl = base.GetMethodDeclaration (method);
4349 protected override string GetTypeName (MethodReturnType returnType)
4351 return GetTypeName (returnType, () => returnType.ReturnType);
4354 string GetTypeName (ICustomAttributeProvider provider, Func<TypeReference> selector)
4356 string type = GetName (selector ());
4357 if (type == "object" && provider.HasCustomAttributes &&
4358 provider.CustomAttributes.Cast<CustomAttribute>()
4359 .Any (ca => ca.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.DynamicAttribute"))
4364 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4366 if (DocUtils.IsExplicitlyImplemented (method)) {
4367 TypeReference iface;
4368 MethodReference ifaceMethod;
4369 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4370 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4372 .Append (ifaceMethod.Name);
4374 return base.AppendMethodName (buf, method);
4377 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4379 if (method.GenericParameters.Count == 0)
4381 return AppendConstraints (buf, method.GenericParameters);
4384 protected override string RefTypeModifier {
4388 protected override string GetFinalizerName (MethodDefinition method)
4390 return "~" + method.DeclaringType.Name + " ()";
4393 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4397 if (method.IsPublic)
4398 return buf.Append ("public");
4399 if (method.IsFamily || method.IsFamilyOrAssembly)
4400 return buf.Append ("protected");
4404 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4406 string modifiers = String.Empty;
4407 if (method.IsStatic) modifiers += " static";
4408 if (method.IsVirtual && !method.IsAbstract) {
4409 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4410 else modifiers += " override";
4412 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4413 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4414 if (method.IsFinal) modifiers += " sealed";
4415 if (modifiers == " virtual sealed") modifiers = "";
4417 return buf.Append (modifiers);
4420 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4422 if (method.IsGenericMethod ()) {
4423 IList<GenericParameter> args = method.GenericParameters;
4424 if (args.Count > 0) {
4426 buf.Append (args [0].Name);
4427 for (int i = 1; i < args.Count; ++i)
4428 buf.Append (",").Append (args [i].Name);
4435 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4437 return AppendParameters (buf, method, parameters, '(', ')');
4440 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4444 if (parameters.Count > 0) {
4445 if (DocUtils.IsExtensionMethod (method))
4446 buf.Append ("this ");
4447 AppendParameter (buf, parameters [0]);
4448 for (int i = 1; i < parameters.Count; ++i) {
4450 AppendParameter (buf, parameters [i]);
4454 return buf.Append (end);
4457 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4459 if (parameter.ParameterType is ByReferenceType) {
4460 if (parameter.IsOut)
4461 buf.Append ("out ");
4463 buf.Append ("ref ");
4465 buf.Append (GetTypeName (parameter, () => parameter.ParameterType)).Append (" ");
4466 return buf.Append (parameter.Name);
4469 protected override string GetPropertyDeclaration (PropertyDefinition property)
4471 MethodDefinition method;
4473 string get_visible = null;
4474 if ((method = property.GetMethod) != null &&
4475 (DocUtils.IsExplicitlyImplemented (method) ||
4476 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
4477 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
4478 string set_visible = null;
4479 if ((method = property.SetMethod) != null &&
4480 (DocUtils.IsExplicitlyImplemented (method) ||
4481 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
4482 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
4484 if ((set_visible == null) && (get_visible == null))
4488 StringBuilder buf = new StringBuilder ();
4489 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
4490 buf.Append (visibility = get_visible);
4491 else if (set_visible != null && get_visible == null)
4492 buf.Append (visibility = set_visible);
4494 buf.Append (visibility = "public");
4496 // Pick an accessor to use for static/virtual/override/etc. checks.
4497 method = property.SetMethod;
4499 method = property.GetMethod;
4501 string modifiers = String.Empty;
4502 if (method.IsStatic) modifiers += " static";
4503 if (method.IsVirtual && !method.IsAbstract) {
4504 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
4505 modifiers += " virtual";
4507 modifiers += " override";
4509 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
4510 if (method.IsAbstract && !declDef.IsInterface)
4511 modifiers += " abstract";
4513 modifiers += " sealed";
4514 if (modifiers == " virtual sealed")
4516 buf.Append (modifiers).Append (' ');
4518 buf.Append (GetName (property.PropertyType)).Append (' ');
4520 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
4521 string name = property.Name;
4522 foreach (MemberReference mi in defs) {
4523 if (mi == property) {
4528 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
4530 if (property.Parameters.Count != 0) {
4531 AppendParameters (buf, method, property.Parameters, '[', ']');
4535 if (set_visible != null) {
4536 if (set_visible != visibility)
4537 buf.Append (' ').Append (set_visible);
4538 buf.Append (" set;");
4540 if (get_visible != null) {
4541 if (get_visible != visibility)
4542 buf.Append (' ').Append (get_visible);
4543 buf.Append (" get;");
4547 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
4550 protected override string GetFieldDeclaration (FieldDefinition field)
4552 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4553 if (declType.IsEnum && field.Name == "value__")
4554 return null; // This member of enums aren't documented.
4556 StringBuilder buf = new StringBuilder ();
4557 AppendFieldVisibility (buf, field);
4558 if (buf.Length == 0)
4561 if (declType.IsEnum)
4564 if (field.IsStatic && !field.IsLiteral)
4565 buf.Append (" static");
4566 if (field.IsInitOnly)
4567 buf.Append (" readonly");
4568 if (field.IsLiteral)
4569 buf.Append (" const");
4571 buf.Append (' ').Append (GetName (field.FieldType)).Append (' ');
4572 buf.Append (field.Name);
4573 AppendFieldValue (buf, field);
4576 return buf.ToString ();
4579 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4582 return buf.Append ("public");
4583 if (field.IsFamily || field.IsFamilyOrAssembly)
4584 return buf.Append ("protected");
4588 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4590 // enums have a value__ field, which we ignore
4591 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
4592 field.DeclaringType.IsGenericType ())
4594 if (field.HasConstant && field.IsLiteral) {
4597 val = field.Constant;
4602 buf.Append (" = ").Append ("null");
4603 else if (val is Enum)
4604 buf.Append (" = ").Append (val.ToString ());
4605 else if (val is IFormattable) {
4606 string value = ((IFormattable)val).ToString();
4608 value = "\"" + value + "\"";
4609 buf.Append (" = ").Append (value);
4615 protected override string GetEventDeclaration (EventDefinition e)
4617 StringBuilder buf = new StringBuilder ();
4618 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4622 AppendModifiers (buf, e.AddMethod);
4624 buf.Append (" event ");
4625 buf.Append (GetName (e.EventType)).Append (' ');
4626 buf.Append (e.Name).Append (';');
4628 return buf.ToString ();
4632 class CSharpMemberFormatter : CSharpFullMemberFormatter {
4633 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4639 class DocTypeFullMemberFormatter : MemberFormatter {
4640 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
4642 protected override char NestedTypeSeparator {
4647 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
4648 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4654 class SlashDocMemberFormatter : MemberFormatter {
4656 protected override char[] GenericTypeContainer {
4657 get {return new char[]{'{', '}'};}
4660 private bool AddTypeCount = true;
4662 private TypeReference genDeclType;
4663 private MethodReference genDeclMethod;
4665 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
4667 if (type is GenericParameter) {
4669 if (genDeclType != null) {
4670 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
4671 for (int i = 0; i < genArgs.Count; ++i) {
4672 if (genArgs [i].Name == type.Name) {
4673 buf.Append ('`').Append (i);
4678 if (genDeclMethod != null) {
4679 IList<GenericParameter> genArgs = null;
4680 if (genDeclMethod.IsGenericMethod ()) {
4681 genArgs = genDeclMethod.GenericParameters;
4682 for (int i = 0; i < genArgs.Count; ++i) {
4683 if (genArgs [i].Name == type.Name) {
4684 buf.Append ("``").Append (i);
4690 if (genDeclType == null && genDeclMethod == null) {
4691 // Probably from within an explicitly implemented interface member,
4692 // where CSC uses parameter names instead of indices (why?), e.g.
4693 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
4694 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
4695 buf.Append (type.Name);
4697 if (buf.Length == l) {
4698 throw new Exception (string.Format (
4699 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
4700 type.Name, genDeclType, genDeclMethod));
4704 base.AppendTypeName (buf, type);
4706 int numArgs = type.GenericParameters.Count;
4707 if (type.DeclaringType != null)
4708 numArgs -= type.GenericParameters.Count;
4710 buf.Append ('`').Append (numArgs);
4717 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
4720 base.AppendGenericType (buf, type);
4722 AppendType (buf, type);
4726 private StringBuilder AppendType (StringBuilder buf, TypeReference type)
4728 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
4729 bool insertNested = false;
4730 int prevParamCount = 0;
4731 foreach (var decl in decls) {
4733 buf.Append (NestedTypeSeparator);
4734 insertNested = true;
4735 base.AppendTypeName (buf, decl);
4736 int argCount = DocUtils.GetGenericArgumentCount (decl);
4737 int numArgs = argCount - prevParamCount;
4738 prevParamCount = argCount;
4740 buf.Append ('`').Append (numArgs);
4745 public override string GetDeclaration (MemberReference member)
4747 TypeReference r = member as TypeReference;
4749 return "T:" + GetTypeName (r);
4751 return base.GetDeclaration (member);
4754 protected override string GetConstructorName (MethodReference constructor)
4756 return GetMethodDefinitionName (constructor, "#ctor");
4759 protected override string GetMethodName (MethodReference method)
4762 MethodDefinition methodDef = method as MethodDefinition;
4763 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
4766 TypeReference iface;
4767 MethodReference ifaceMethod;
4768 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
4769 AddTypeCount = false;
4770 name = GetTypeName (iface) + "." + ifaceMethod.Name;
4771 AddTypeCount = true;
4773 return GetMethodDefinitionName (method, name);
4776 private string GetMethodDefinitionName (MethodReference method, string name)
4778 StringBuilder buf = new StringBuilder ();
4779 buf.Append (GetTypeName (method.DeclaringType));
4781 buf.Append (name.Replace (".", "#"));
4782 if (method.IsGenericMethod ()) {
4783 IList<GenericParameter> genArgs = method.GenericParameters;
4784 if (genArgs.Count > 0)
4785 buf.Append ("``").Append (genArgs.Count);
4787 IList<ParameterDefinition> parameters = method.Parameters;
4789 genDeclType = method.DeclaringType;
4790 genDeclMethod = method;
4791 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
4795 genDeclMethod = null;
4797 return buf.ToString ();
4800 private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
4802 if (parameters.Count == 0)
4807 AppendParameter (buf, genArgs, parameters [0]);
4808 for (int i = 1; i < parameters.Count; ++i) {
4810 AppendParameter (buf, genArgs, parameters [i]);
4813 return buf.Append (')');
4816 private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
4818 AddTypeCount = false;
4819 buf.Append (GetTypeName (parameter.ParameterType));
4820 AddTypeCount = true;
4824 protected override string GetPropertyName (PropertyReference property)
4828 PropertyDefinition propertyDef = property as PropertyDefinition;
4829 MethodDefinition method = null;
4830 if (propertyDef != null)
4831 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
4832 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
4833 name = property.Name;
4835 TypeReference iface;
4836 MethodReference ifaceMethod;
4837 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4838 AddTypeCount = false;
4839 name = string.Join ("#", new string[]{
4840 GetTypeName (iface).Replace (".", "#"),
4841 DocUtils.GetMember (property.Name)
4843 AddTypeCount = true;
4846 StringBuilder buf = new StringBuilder ();
4847 buf.Append (GetName (property.DeclaringType));
4850 IList<ParameterDefinition> parameters = property.Parameters;
4851 if (parameters.Count > 0) {
4852 genDeclType = property.DeclaringType;
4854 IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
4855 AppendParameter (buf, genArgs, parameters [0]);
4856 for (int i = 1; i < parameters.Count; ++i) {
4858 AppendParameter (buf, genArgs, parameters [i]);
4863 return buf.ToString ();
4866 protected override string GetFieldName (FieldReference field)
4868 return string.Format ("{0}.{1}",
4869 GetName (field.DeclaringType), field.Name);
4872 protected override string GetEventName (EventReference e)
4874 return string.Format ("{0}.{1}",
4875 GetName (e.DeclaringType), e.Name);
4878 protected override string GetTypeDeclaration (TypeDefinition type)
4880 string name = GetName (type);
4886 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4888 string name = GetName (constructor);
4894 protected override string GetMethodDeclaration (MethodDefinition method)
4896 string name = GetName (method);
4899 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4900 genDeclType = method.DeclaringType;
4901 genDeclMethod = method;
4902 name += "~" + GetName (method.ReturnType);
4904 genDeclMethod = null;
4909 protected override string GetPropertyDeclaration (PropertyDefinition property)
4911 string name = GetName (property);
4917 protected override string GetFieldDeclaration (FieldDefinition field)
4919 string name = GetName (field);
4925 protected override string GetEventDeclaration (EventDefinition e)
4927 string name = GetName (e);
4934 class FileNameMemberFormatter : SlashDocMemberFormatter {
4935 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4940 protected override char NestedTypeSeparator {