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;
65 var types = new List<string> ();
66 var p = new OptionSet () {
68 "Delete removed members from the XML files.",
69 v => delete = v != null },
71 "Document potential exceptions that members can generate. {SOURCES} " +
72 "is a comma-separated list of:\n" +
73 " asm Method calls in same assembly\n" +
74 " depasm Method calls in dependent assemblies\n" +
75 " all Record all possible exceptions\n" +
76 " added Modifier; only create <exception/>s\n" +
77 " for NEW types/members\n" +
78 "If nothing is specified, then only exceptions from the member will " +
80 v => exceptions = ParseExceptionLocations (v) },
82 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
85 case "ignore-missing-types":
86 ignore_missing_types = true;
88 case "no-assembly-versions":
89 no_assembly_versions = true;
92 throw new Exception ("Unsupported flag `" + v + "'.");
95 { "fignore-missing-types",
96 "Do not report an error if a --type=TYPE type\nwas not found.",
97 v => ignore_missing_types = v != null },
98 { "fno-assembly-versions",
99 "Do not generate //AssemblyVersion elements.",
100 v => no_assembly_versions = v != null },
102 "Import documentation from {FILE}.",
103 v => AddImporter (v) },
105 "Check for assembly references in {DIRECTORY}.",
106 v => assemblyResolver.AddSearchDirectory (v) },
108 "Ignored for compatibility with update-ecma-xml.",
111 "Root {DIRECTORY} to generate/update documentation.",
114 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
115 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
116 v => assemblyResolver.AddSearchDirectory (Path.GetDirectoryName (v)) },
118 "Manually specify the assembly {VERSION} that new members were added in.",
121 "Only update documentation for {TYPE}.",
122 v => types.Add (v) },
124 var assemblies = Parse (p, args, "update",
125 "[OPTIONS]+ ASSEMBLIES",
126 "Create or update documentation from ASSEMBLIES.");
127 if (assemblies == null)
129 if (assemblies.Count == 0)
130 Error ("No assemblies specified.");
132 foreach (var dir in assemblies
133 .Where (a => a.Contains (Path.DirectorySeparatorChar))
134 .Select (a => Path.GetDirectoryName (a)))
135 assemblyResolver.AddSearchDirectory (dir);
137 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
140 throw new InvalidOperationException("The --out option is required.");
142 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
144 docEnum = docEnum ?? new DocumentationEnumerator ();
146 // PERFORM THE UPDATES
148 if (types.Count > 0) {
150 DoUpdateTypes (srcPath, types, srcPath);
153 else if (opts.@namespace != null)
154 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
155 Path.Combine (dest_dir, opts.@namespace));
158 DoUpdateAssemblies (srcPath, srcPath);
160 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
163 void AddImporter (string path)
166 XmlReader r = new XmlTextReader (path);
168 while (r.NodeType != XmlNodeType.Element) {
170 Error ("Unable to read XML file: {0}.", path);
172 if (r.LocalName == "doc") {
173 importers.Add (new MsxdocDocumentationImporter (path));
175 else if (r.LocalName == "Libraries") {
176 var ecmadocs = new XmlTextReader (path);
177 docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
178 importers.Add (new EcmaDocumentationImporter (ecmadocs));
181 Error ("Unsupported XML format within {0}.", path);
184 } catch (Exception e) {
185 Environment.ExitCode = 1;
186 Error ("Could not load XML file: {0}.", e.Message);
190 static ExceptionLocations ParseExceptionLocations (string s)
192 ExceptionLocations loc = ExceptionLocations.Member;
195 foreach (var type in s.Split (',')) {
197 case "added": loc |= ExceptionLocations.AddedMembers; break;
198 case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
199 case "asm": loc |= ExceptionLocations.Assembly; break;
200 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
201 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
207 internal void Warning (string format, params object[] args)
209 Message (TraceLevel.Warning, "mdoc: " + format, args);
212 private AssemblyDefinition LoadAssembly (string name)
214 AssemblyDefinition assembly = null;
216 assembly = AssemblyDefinition.ReadAssembly (name, new ReaderParameters { AssemblyResolver = assemblyResolver });
217 } catch (System.IO.FileNotFoundException) { }
219 if (assembly == null)
220 throw new InvalidOperationException("Assembly " + name + " not found.");
225 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
226 OrderTypeAttributes (element);
227 XmlTextWriter writer = new XmlTextWriter(output);
228 writer.Formatting = Formatting.Indented;
229 writer.Indentation = 2;
230 writer.IndentChar = ' ';
231 element.WriteTo(writer);
235 private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
237 Action<string> creator = file => {
238 using (var writer = OpenWrite (file, mode))
242 MdocFile.UpdateFile (filename, creator);
245 private static void OrderTypeAttributes (XmlElement e)
247 foreach (XmlElement type in e.SelectNodes ("//Type")) {
248 OrderTypeAttributes (type.Attributes);
252 static readonly string[] TypeAttributeOrder = {
253 "Name", "FullName", "FullNameSP", "Maintainer"
256 private static void OrderTypeAttributes (XmlAttributeCollection c)
258 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
259 for (int i = 0; i < c.Count; ++i) {
260 XmlAttribute a = c [i];
261 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
262 if (a.Name == TypeAttributeOrder [j]) {
268 for (int i = attrs.Length-1; i >= 0; --i) {
269 XmlAttribute n = attrs [i];
272 XmlAttribute r = null;
273 for (int j = i+1; j < attrs.Length; ++j) {
274 if (attrs [j] != null) {
282 c.InsertBefore (n, r);
286 private XmlDocument CreateIndexStub()
288 XmlDocument index = new XmlDocument();
290 XmlElement index_root = index.CreateElement("Overview");
291 index.AppendChild(index_root);
293 if (assemblies.Count == 0)
294 throw new Exception ("No assembly");
296 XmlElement index_assemblies = index.CreateElement("Assemblies");
297 index_root.AppendChild(index_assemblies);
299 XmlElement index_remarks = index.CreateElement("Remarks");
300 index_remarks.InnerText = "To be added.";
301 index_root.AppendChild(index_remarks);
303 XmlElement index_copyright = index.CreateElement("Copyright");
304 index_copyright.InnerText = "To be added.";
305 index_root.AppendChild(index_copyright);
307 XmlElement index_types = index.CreateElement("Types");
308 index_root.AppendChild(index_types);
313 private static void WriteNamespaceStub(string ns, string outdir) {
314 XmlDocument index = new XmlDocument();
316 XmlElement index_root = index.CreateElement("Namespace");
317 index.AppendChild(index_root);
319 index_root.SetAttribute("Name", ns);
321 XmlElement index_docs = index.CreateElement("Docs");
322 index_root.AppendChild(index_docs);
324 XmlElement index_summary = index.CreateElement("summary");
325 index_summary.InnerText = "To be added.";
326 index_docs.AppendChild(index_summary);
328 XmlElement index_remarks = index.CreateElement("remarks");
329 index_remarks.InnerText = "To be added.";
330 index_docs.AppendChild(index_remarks);
332 WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
333 writer => WriteXml (index.DocumentElement, writer));
336 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
338 var index = CreateIndexForTypes (dest);
340 var found = new HashSet<string> ();
341 foreach (AssemblyDefinition assembly in assemblies) {
342 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames)) {
343 string relpath = DoUpdateType (type, basepath, dest);
347 found.Add (type.FullName);
352 index.Add (assembly);
360 if (ignore_missing_types)
363 var notFound = from n in typenames where !found.Contains (n) select n;
365 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
368 class IndexForTypes {
374 XmlElement index_types;
375 XmlElement index_assemblies;
377 public IndexForTypes (MDocUpdater app, string indexFile, XmlDocument index)
380 this.indexFile = indexFile;
383 index_types = WriteElement (index.DocumentElement, "Types");
384 index_assemblies = WriteElement (index.DocumentElement, "Assemblies");
387 public void Add (AssemblyDefinition assembly)
389 if (index_assemblies.SelectSingleNode ("Assembly[@Name='" + assembly.Name.Name + "']") != null)
392 app.AddIndexAssembly (assembly, index_assemblies);
395 public void Add (TypeDefinition type)
397 app.AddIndexType (type, index_types);
402 SortIndexEntries (index_types);
403 WriteFile (indexFile, FileMode.Create,
404 writer => WriteXml (index.DocumentElement, writer));
408 IndexForTypes CreateIndexForTypes (string dest)
410 string indexFile = Path.Combine (dest, "index.xml");
411 if (File.Exists (indexFile))
413 return new IndexForTypes (this, indexFile, CreateIndexStub ());
416 public string DoUpdateType (TypeDefinition type, string basepath, string dest)
418 if (type.Namespace == null)
419 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
421 if (!IsPublic (type))
424 // Must get the A+B form of the type name.
425 string typename = GetTypeFileName(type);
427 string reltypefile = DocUtils.PathCombine (DocUtils.GetNamespace (type), typename + ".xml");
428 string typefile = Path.Combine (basepath, reltypefile);
429 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
431 string output = null;
434 } else if (dest == "-") {
437 output = Path.Combine (dest, reltypefile);
442 XmlDocument basefile = new XmlDocument();
444 basefile.Load(typefile);
445 } catch (Exception e) {
446 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
449 DoUpdateType2("Updating", basefile, type, output, false);
452 XmlElement td = StubType(type, output);
456 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
459 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
465 public void DoUpdateNS (string ns, string nspath, string outpath)
467 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
468 AssemblyDefinition assembly = assemblies [0];
470 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
471 XmlDocument basefile = new XmlDocument();
472 string typefile = Path.Combine(nspath, file.Name);
474 basefile.Load(typefile);
475 } catch (Exception e) {
476 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
480 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
481 TypeDefinition type = assembly.GetType(typename);
483 Warning ("Type no longer in assembly: " + typename);
487 seenTypes[type] = seenTypes;
488 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false);
491 // Stub types not in the directory
492 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
493 if (type.Namespace != ns || seenTypes.ContainsKey(type))
496 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"));
497 if (td == null) continue;
501 private static string GetTypeFileName (TypeReference type)
503 return filenameFormatter.GetName (type);
506 public static string GetTypeFileName (string typename)
508 StringBuilder filename = new StringBuilder (typename.Length);
512 for (int i = 0; i < typename.Length; ++i) {
513 char c = typename [i];
522 filename.Append ('`').Append ((numArgs+1).ToString());
537 return filename.ToString ();
540 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
542 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
543 index_assembly.SetAttribute ("Name", assembly.Name.Name);
544 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
546 AssemblyNameDefinition name = assembly.Name;
547 if (name.HasPublicKey) {
548 XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
549 var key = new StringBuilder (name.PublicKey.Length*3 + 2);
551 foreach (byte b in name.PublicKey)
552 key.AppendFormat ("{0,2:x2} ", b);
554 pubkey.InnerText = key.ToString ();
555 index_assembly.AppendChild (pubkey);
558 if (!string.IsNullOrEmpty (name.Culture)) {
559 XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
560 culture.InnerText = name.Culture;
561 index_assembly.AppendChild (culture);
564 MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
565 parent.AppendChild(index_assembly);
568 private void AddIndexType (TypeDefinition type, XmlElement index_types)
570 string typename = GetTypeFileName(type);
572 // Add namespace and type nodes into the index file as needed
573 string ns = DocUtils.GetNamespace (type);
574 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode ("Namespace[@Name='" + ns + "']");
575 if (nsnode == null) {
576 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
577 nsnode.SetAttribute ("Name", ns);
578 index_types.AppendChild (nsnode);
580 string doc_typename = GetDocTypeName (type);
581 XmlElement typenode = (XmlElement) nsnode.SelectSingleNode ("Type[@Name='" + typename + "']");
582 if (typenode == null) {
583 typenode = index_types.OwnerDocument.CreateElement ("Type");
584 typenode.SetAttribute ("Name", typename);
585 nsnode.AppendChild (typenode);
587 if (typename != doc_typename)
588 typenode.SetAttribute("DisplayName", doc_typename);
590 typenode.RemoveAttribute("DisplayName");
592 typenode.SetAttribute ("Kind", GetTypeKind (type));
595 private void DoUpdateAssemblies (string source, string dest)
597 string indexfile = dest + "/index.xml";
599 if (System.IO.File.Exists(indexfile)) {
600 index = new XmlDocument();
601 index.Load(indexfile);
604 ClearElement(index.DocumentElement, "Assembly");
605 ClearElement(index.DocumentElement, "Attributes");
607 index = CreateIndexStub();
610 string defaultTitle = "Untitled";
611 if (assemblies.Count == 1)
612 defaultTitle = assemblies[0].Name.Name;
613 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
615 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
616 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
617 index_assemblies.RemoveAll ();
620 HashSet<string> goodfiles = new HashSet<string> ();
622 foreach (AssemblyDefinition assm in assemblies) {
623 AddIndexAssembly (assm, index_assemblies);
624 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
627 SortIndexEntries (index_types);
629 CleanupFiles (dest, goodfiles);
630 CleanupIndexTypes (index_types, goodfiles);
631 CleanupExtensions (index_types);
633 WriteFile (indexfile, FileMode.Create,
634 writer => WriteXml(index.DocumentElement, writer));
637 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
639 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
641 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
642 string typename = GetTypeFileName(type);
643 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0)
646 string reltypepath = DoUpdateType (type, source, dest);
647 if (reltypepath == null)
650 // Add namespace and type nodes into the index file as needed
651 AddIndexType (type, index_types);
653 // Ensure the namespace index file exists
654 string onsdoc = DocUtils.PathCombine (dest, type.Namespace + ".xml");
655 string nsdoc = DocUtils.PathCombine (dest, "ns-" + type.Namespace + ".xml");
656 if (File.Exists (onsdoc)) {
657 File.Move (onsdoc, nsdoc);
660 if (!File.Exists (nsdoc)) {
661 Console.WriteLine("New Namespace File: " + type.Namespace);
662 WriteNamespaceStub(type.Namespace, dest);
665 goodfiles.Add (reltypepath);
669 private static void SortIndexEntries (XmlElement indexTypes)
671 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
672 XmlNodeComparer c = new AttributeNameComparer ();
673 SortXmlNodes (indexTypes, namespaces, c);
675 for (int i = 0; i < namespaces.Count; ++i)
676 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
679 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
681 MyXmlNodeList l = new MyXmlNodeList (children.Count);
682 for (int i = 0; i < children.Count; ++i)
683 l.Add (children [i]);
685 for (int i = l.Count - 1; i > 0; --i) {
686 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
690 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
692 public abstract int Compare (XmlNode x, XmlNode y);
694 public int Compare (object x, object y)
696 return Compare ((XmlNode) x, (XmlNode) y);
700 class AttributeNameComparer : XmlNodeComparer {
703 public AttributeNameComparer ()
708 public AttributeNameComparer (string attribute)
710 this.attribute = attribute;
713 public override int Compare (XmlNode x, XmlNode y)
715 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
719 class VersionComparer : XmlNodeComparer {
720 public override int Compare (XmlNode x, XmlNode y)
722 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
723 string a = GetVersion (x.InnerText);
724 string b = GetVersion (y.InnerText);
725 return new Version (a).CompareTo (new Version (b));
728 static string GetVersion (string v)
730 int n = v.IndexOf ("x");
733 return v.Substring (0, n-1);
737 private static string GetTypeKind (TypeDefinition type)
740 return "Enumeration";
741 if (type.IsValueType)
743 if (type.IsInterface)
745 if (DocUtils.IsDelegate (type))
747 if (type.IsClass || type.FullName == "System.Enum") // FIXME
749 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
752 private static bool IsPublic (TypeDefinition type)
754 TypeDefinition decl = type;
755 while (decl != null) {
756 if (!(decl.IsPublic || decl.IsNestedPublic)) {
759 decl = (TypeDefinition) decl.DeclaringType;
764 private void CleanupFiles (string dest, HashSet<string> goodfiles)
766 // Look for files that no longer correspond to types
767 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
768 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
769 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
770 if (!goodfiles.Contains (relTypeFile)) {
771 XmlDocument doc = new XmlDocument ();
772 doc.Load (typefile.FullName);
773 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
774 if (UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
775 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
776 WriteXml(doc.DocumentElement, writer);
777 goodfiles.Add (relTypeFile);
780 string newname = typefile.FullName + ".remove";
781 try { System.IO.File.Delete(newname); } catch (Exception) { }
782 try { typefile.MoveTo(newname); } catch (Exception) { }
783 Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
789 private static TextWriter OpenWrite (string path, FileMode mode)
791 var w = new StreamWriter (
792 new FileStream (path, mode),
793 new UTF8Encoding (false)
799 private string[] GetAssemblyVersions ()
801 return (from a in assemblies select GetAssemblyVersion (a)).ToArray ();
804 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
806 // Look for type nodes that no longer correspond to types
807 MyXmlNodeList remove = new MyXmlNodeList ();
808 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
809 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
810 if (!goodfiles.Contains (fulltypename)) {
811 remove.Add (typenode);
814 foreach (XmlNode n in remove)
815 n.ParentNode.RemoveChild (n);
818 private void CleanupExtensions (XmlElement index_types)
820 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
821 if (extensionMethods.Count == 0) {
824 index_types.SelectSingleNode ("/Overview").RemoveChild (e);
828 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
829 index_types.SelectSingleNode ("/Overview").AppendChild (e);
833 extensionMethods.Sort (DefaultExtensionMethodComparer);
834 foreach (XmlNode m in extensionMethods) {
835 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
839 class ExtensionMethodComparer : XmlNodeComparer {
840 public override int Compare (XmlNode x, XmlNode y)
842 XmlNode xLink = x.SelectSingleNode ("Member/Link");
843 XmlNode yLink = y.SelectSingleNode ("Member/Link");
845 int n = xLink.Attributes ["Type"].Value.CompareTo (
846 yLink.Attributes ["Type"].Value);
849 n = xLink.Attributes ["Member"].Value.CompareTo (
850 yLink.Attributes ["Member"].Value);
851 if (n == 0 && !object.ReferenceEquals (x, y))
852 throw new InvalidOperationException ("Duplicate extension method found!");
857 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
859 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince)
861 Console.WriteLine(message + ": " + type.FullName);
863 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
865 // Update type metadata
866 UpdateType(basefile.DocumentElement, type);
868 // Update existing members. Delete member nodes that no longer should be there,
869 // and remember what members are already documented so we don't add them again.
871 MyXmlNodeList todelete = new MyXmlNodeList ();
872 foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
873 XmlElement oldmember = info.Node;
874 MemberReference oldmember2 = info.Member;
875 string sig = oldmember2 != null ? memberFormatters [0].GetDeclaration (oldmember2) : null;
877 // Interface implementations and overrides are deleted from the docs
878 // unless the overrides option is given.
879 if (oldmember2 != null && sig == null)
882 // Deleted (or signature changed)
883 if (oldmember2 == null) {
884 if (UpdateAssemblyVersions (oldmember, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
886 DeleteMember ("Member Removed", output, oldmember, todelete);
891 if (seenmembers.ContainsKey (sig)) {
892 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
893 // ignore, already seen
895 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
896 DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
898 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
902 // Update signature information
905 seenmembers.Add (sig, oldmember);
907 foreach (XmlElement oldmember in todelete)
908 oldmember.ParentNode.RemoveChild (oldmember);
911 if (!DocUtils.IsDelegate (type)) {
912 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
913 foreach (MemberReference m in type.GetMembers()) {
914 if (m is TypeDefinition) continue;
916 string sig = memberFormatters [0].GetDeclaration (m);
917 if (sig == null) continue;
918 if (seenmembers.ContainsKey(sig)) continue;
920 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
921 if (mm == null) continue;
922 members.AppendChild( mm );
924 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
929 // Import code snippets from files
930 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
931 if (!(code is XmlElement)) continue;
932 string file = ((XmlElement)code).GetAttribute("src");
933 string lang = ((XmlElement)code).GetAttribute("lang");
935 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
937 code.InnerText = src;
941 if (insertSince && since != null) {
942 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
943 docs.AppendChild (CreateSinceNode (basefile));
947 XmlElement d = basefile.DocumentElement ["Docs"];
948 XmlElement m = basefile.DocumentElement ["Members"];
949 if (d != null && m != null)
950 basefile.DocumentElement.InsertBefore (
951 basefile.DocumentElement.RemoveChild (d), m);
956 WriteXml(basefile.DocumentElement, Console.Out);
958 FileInfo file = new FileInfo (output);
959 if (!file.Directory.Exists) {
960 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
961 file.Directory.Create ();
963 WriteFile (output, FileMode.Create,
964 writer => WriteXml(basefile.DocumentElement, writer));
968 private string GetCodeSource (string lang, string file)
971 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
972 // Grab the specified region
973 string region = "#region " + file.Substring (anchorStart + 4);
974 file = file.Substring (0, anchorStart + 3);
976 using (StreamReader reader = new StreamReader (file)) {
978 StringBuilder src = new StringBuilder ();
980 while ((line = reader.ReadLine ()) != null) {
981 if (line.Trim() == region) {
982 indent = line.IndexOf (region);
985 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
990 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
993 return src.ToString ();
995 } catch (Exception e) {
996 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
997 file, region, show_exceptions ? e.ToString () : e.Message);
1002 using (StreamReader reader = new StreamReader (file))
1003 return reader.ReadToEnd ();
1004 } catch (Exception e) {
1005 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
1010 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
1012 string format = output != null
1013 ? "{0}: File='{1}'; Signature='{4}'"
1014 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1018 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1019 member.Attributes ["MemberName"].Value,
1020 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
1021 if (!delete && MemberDocsHaveUserContent (member)) {
1022 Warning ("Member deletions must be enabled with the --delete option.");
1024 todelete.Add (member);
1029 class MemberComparer : XmlNodeComparer {
1030 public override int Compare (XmlNode x, XmlNode y)
1033 string xMemberName = x.Attributes ["MemberName"].Value;
1034 string yMemberName = y.Attributes ["MemberName"].Value;
1036 // generic methods *end* with '>'
1037 // it's possible for explicitly implemented generic interfaces to
1038 // contain <...> without being a generic method
1039 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1040 (r = xMemberName.CompareTo (yMemberName)) != 0)
1044 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1045 xMemberName = xMemberName.Substring (0, lt);
1046 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1047 yMemberName = yMemberName.Substring (0, lt);
1048 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1051 // if @MemberName matches, then it's either two different types of
1052 // members sharing the same name, e.g. field & property, or it's an
1053 // overloaded method.
1054 // for different type, sort based on MemberType value.
1055 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1056 y.SelectSingleNode ("MemberType").InnerText);
1060 // same type -- must be an overloaded method. Sort based on type
1061 // parameter count, then parameter count, then by the parameter
1063 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1064 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1065 if (xTypeParams.Count != yTypeParams.Count)
1066 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1067 for (int i = 0; i < xTypeParams.Count; ++i) {
1068 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1069 yTypeParams [i].Attributes ["Name"].Value);
1074 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1075 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1076 if (xParams.Count != yParams.Count)
1077 return xParams.Count <= yParams.Count ? -1 : 1;
1078 for (int i = 0; i < xParams.Count; ++i) {
1079 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1080 yParams [i].Attributes ["Type"].Value);
1084 // all parameters match, but return value might not match if it was
1085 // changed between one version and another.
1086 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1087 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1088 if (xReturn != null && yReturn != null) {
1089 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1098 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1100 private static void SortTypeMembers (XmlNode members)
1102 if (members == null)
1104 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1107 private static bool MemberDocsHaveUserContent (XmlNode e)
1109 e = (XmlElement)e.SelectSingleNode("Docs");
1110 if (e == null) return false;
1111 foreach (XmlElement d in e.SelectNodes("*"))
1112 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1117 // UPDATE HELPER FUNCTIONS
1119 // CREATE A STUB DOCUMENTATION FILE
1121 public XmlElement StubType (TypeDefinition type, string output)
1123 string typesig = typeFormatters [0].GetDeclaration (type);
1124 if (typesig == null) return null; // not publicly visible
1126 XmlDocument doc = new XmlDocument();
1127 XmlElement root = doc.CreateElement("Type");
1128 doc.AppendChild (root);
1130 DoUpdateType2 ("New Type", doc, type, output, true);
1135 private XmlElement CreateSinceNode (XmlDocument doc)
1137 XmlElement s = doc.CreateElement ("since");
1138 s.SetAttribute ("version", since);
1142 // STUBBING/UPDATING FUNCTIONS
1144 public void UpdateType (XmlElement root, TypeDefinition type)
1146 root.SetAttribute("Name", GetDocTypeName (type));
1147 root.SetAttribute("FullName", GetDocTypeFullName (type));
1149 foreach (MemberFormatter f in typeFormatters) {
1150 string element = "TypeSignature[@Language='" + f.Language + "']";
1151 WriteElementAttribute (root, element, "Language", f.Language);
1152 WriteElementAttribute (root, element, "Value", f.GetDeclaration (type));
1155 XmlElement ass = WriteElement(root, "AssemblyInfo");
1156 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1157 if (!no_assembly_versions) {
1158 UpdateAssemblyVersions (root, type, true);
1161 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1162 foreach (var version in versions)
1163 ass.RemoveChild (version);
1165 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1166 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1168 ClearElement(ass, "AssemblyCulture");
1170 // Why-oh-why do we put assembly attributes in each type file?
1171 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1172 // since they're outdated in current docs, and a waste of space.
1173 //MakeAttributes(ass, type.Assembly, true);
1174 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1175 if (assattrs != null)
1176 ass.RemoveChild(assattrs);
1178 NormalizeWhitespace(ass);
1180 if (type.IsGenericType ()) {
1181 MakeTypeParameters (root, type.GenericParameters);
1183 ClearElement(root, "TypeParameters");
1186 if (type.BaseType != null) {
1187 XmlElement basenode = WriteElement(root, "Base");
1189 string basetypename = GetDocTypeFullName (type.BaseType);
1190 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1191 WriteElementText(root, "Base/BaseTypeName", basetypename);
1193 // Document how this type instantiates the generic parameters of its base type
1194 TypeReference origBase = type.BaseType.GetElementType ();
1195 if (origBase.IsGenericType ()) {
1196 ClearElement(basenode, "BaseTypeArguments");
1197 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1198 IList<TypeReference> baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1199 IList<GenericParameter> baseGenParams = origBase.GenericParameters;
1200 if (baseGenArgs.Count != baseGenParams.Count)
1201 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1202 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1203 GenericParameter param = baseGenParams [i];
1204 TypeReference value = baseGenArgs [i];
1206 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1207 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1208 bta.AppendChild(arg);
1209 arg.SetAttribute ("TypeParamName", param.Name);
1210 arg.InnerText = GetDocTypeFullName (value);
1214 ClearElement(root, "Base");
1217 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1218 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1219 List<string> interface_names = userInterfaces
1220 .Select (iface => GetDocTypeFullName (iface))
1224 XmlElement interfaces = WriteElement(root, "Interfaces");
1225 interfaces.RemoveAll();
1226 foreach (string iname in interface_names) {
1227 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1228 interfaces.AppendChild(iface);
1229 WriteElementText(iface, "InterfaceName", iname);
1232 ClearElement(root, "Interfaces");
1235 MakeAttributes (root, GetCustomAttributes (type));
1237 if (DocUtils.IsDelegate (type)) {
1238 MakeTypeParameters (root, type.GenericParameters);
1239 MakeParameters(root, type.GetMethod("Invoke").Parameters);
1240 MakeReturnValue(root, type.GetMethod("Invoke"));
1243 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1244 MakeDocNode (typeInfo);
1246 if (!DocUtils.IsDelegate (type))
1247 WriteElement (root, "Members");
1249 OrderTypeNodes (root, root.ChildNodes);
1250 NormalizeWhitespace(root);
1253 static readonly string[] TypeNodeOrder = {
1257 "ThreadingSafetyStatement",
1258 "ThreadSafetyStatement",
1270 static void OrderTypeNodes (XmlNode member, XmlNodeList children)
1272 ReorderNodes (member, children, TypeNodeOrder);
1275 internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1277 List<T> l = new List<T> (list);
1282 private void UpdateMember (DocsNodeInfo info)
1284 XmlElement me = (XmlElement) info.Node;
1285 MemberReference mi = info.Member;
1287 foreach (MemberFormatter f in memberFormatters) {
1288 string element = "MemberSignature[@Language='" + f.Language + "']";
1289 WriteElementAttribute (me, element, "Language", f.Language);
1290 WriteElementAttribute (me, element, "Value", f.GetDeclaration (mi));
1293 WriteElementText(me, "MemberType", GetMemberType(mi));
1295 if (!no_assembly_versions) {
1296 UpdateAssemblyVersions (me, mi, true);
1299 ClearElement (me, "AssemblyInfo");
1302 MakeAttributes (me, GetCustomAttributes (mi));
1304 MakeReturnValue(me, mi);
1305 if (mi is MethodReference) {
1306 MethodReference mb = (MethodReference) mi;
1307 if (mb.IsGenericMethod ())
1308 MakeTypeParameters (me, mb.GenericParameters);
1310 MakeParameters(me, mi);
1313 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1314 WriteElementText(me, "MemberValue", fieldValue);
1316 info.Node = WriteElement (me, "Docs");
1318 OrderMemberNodes (me, me.ChildNodes);
1319 UpdateExtensionMethods (me, info);
1322 static readonly string[] MemberNodeOrder = {
1337 static void OrderMemberNodes (XmlNode member, XmlNodeList children)
1339 ReorderNodes (member, children, MemberNodeOrder);
1342 static void ReorderNodes (XmlNode node, XmlNodeList children, string[] ordering)
1344 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1345 for (int i = 0; i < ordering.Length; ++i) {
1346 for (int j = 0; j < children.Count; ++j) {
1347 XmlNode c = children [j];
1348 if (c.Name == ordering [i]) {
1349 newChildren.Add (c);
1353 if (newChildren.Count >= 0)
1354 node.PrependChild ((XmlNode) newChildren [0]);
1355 for (int i = 1; i < newChildren.Count; ++i) {
1356 XmlNode prev = (XmlNode) newChildren [i-1];
1357 XmlNode cur = (XmlNode) newChildren [i];
1358 node.RemoveChild (cur);
1359 node.InsertAfter (cur, prev);
1363 IEnumerable<string> GetCustomAttributes (MemberReference mi)
1365 IEnumerable<string> attrs = Enumerable.Empty<string>();
1367 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1369 attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1371 PropertyDefinition pd = mi as PropertyDefinition;
1373 if (pd.GetMethod != null)
1374 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1375 if (pd.SetMethod != null)
1376 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1379 EventDefinition ed = mi as EventDefinition;
1381 if (ed.AddMethod != null)
1382 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1383 if (ed.RemoveMethod != null)
1384 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1390 IEnumerable<string> GetCustomAttributes (IList<CustomAttribute> attributes, string prefix)
1392 foreach (CustomAttribute attribute in attributes.OrderBy (ca => ca.AttributeType.FullName)) {
1394 TypeDefinition attrType = attribute.AttributeType as TypeDefinition;
1395 if (attrType != null && !IsPublic (attrType))
1397 if (slashdocFormatter.GetName (attribute.AttributeType) == null)
1400 if (Array.IndexOf (IgnorableAttributes, attribute.AttributeType.FullName) >= 0)
1403 StringList fields = new StringList ();
1405 for (int i = 0; i < attribute.ConstructorArguments.Count; ++i) {
1406 CustomAttributeArgument argument = attribute.ConstructorArguments [i];
1407 fields.Add (MakeAttributesValueString (
1412 (from namedArg in attribute.Fields
1413 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value })
1415 (from namedArg in attribute.Properties
1416 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value }))
1417 .OrderBy (v => v.Name);
1418 foreach (var d in namedArgs)
1419 fields.Add (string.Format ("{0}={1}", d.Name,
1420 MakeAttributesValueString (d.Value, d.Type)));
1422 string a2 = String.Join(", ", fields.ToArray ());
1423 if (a2 != "") a2 = "(" + a2 + ")";
1425 string name = attribute.Constructor.DeclaringType.FullName;
1426 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1427 yield return prefix + name + a2;
1431 static readonly string[] ValidExtensionMembers = {
1440 static readonly string[] ValidExtensionDocMembers = {
1446 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1448 MethodDefinition me = info.Member as MethodDefinition;
1451 if (info.Parameters.Count < 1)
1453 if (!DocUtils.IsExtensionMethod (me))
1456 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1457 XmlNode member = e.CloneNode (true);
1458 em.AppendChild (member);
1459 RemoveExcept (member, ValidExtensionMembers);
1460 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1461 WriteElementText (member, "MemberType", "ExtensionMethod");
1462 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1463 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1464 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1465 member.AppendChild (link);
1466 AddTargets (em, info);
1468 extensionMethods.Add (em);
1471 private static void RemoveExcept (XmlNode node, string[] except)
1475 MyXmlNodeList remove = null;
1476 foreach (XmlNode n in node.ChildNodes) {
1477 if (Array.BinarySearch (except, n.Name) < 0) {
1479 remove = new MyXmlNodeList ();
1484 foreach (XmlNode n in remove)
1485 node.RemoveChild (n);
1488 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1490 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1491 member.PrependChild (targets);
1492 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1493 AppendElementAttributeText (targets, "Target", "Type",
1494 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1497 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1498 IList<TypeReference> constraints = gp.Constraints;
1499 if (constraints.Count == 0)
1500 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1502 foreach (TypeReference c in constraints)
1503 AppendElementAttributeText(targets, "Target", "Type",
1504 slashdocFormatter.GetDeclaration (c));
1508 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1511 TypeDefinition type = field.DeclaringType.Resolve ();
1512 if (type != null && type.IsEnum) return false;
1514 if (type != null && type.IsGenericType ()) return false;
1515 if (!field.HasConstant)
1517 if (field.IsLiteral) {
1518 object val = field.Constant;
1519 if (val == null) value = "null";
1520 else if (val is Enum) value = val.ToString();
1521 else if (val is IFormattable) {
1522 value = ((IFormattable)val).ToString();
1524 value = "\"" + value + "\"";
1526 if (value != null && value != "")
1532 // XML HELPER FUNCTIONS
1534 internal static XmlElement WriteElement(XmlNode parent, string element) {
1535 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1537 string[] path = element.Split('/');
1538 foreach (string p in path) {
1539 ret = (XmlElement)parent.SelectSingleNode(p);
1542 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1543 ename = ename.Substring(0, ename.IndexOf('['));
1544 ret = parent.OwnerDocument.CreateElement(ename);
1545 parent.AppendChild(ret);
1554 private static void WriteElementText(XmlNode parent, string element, string value) {
1555 XmlElement node = WriteElement(parent, element);
1556 node.InnerText = value;
1559 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1561 XmlElement n = parent.OwnerDocument.CreateElement (element);
1562 parent.AppendChild (n);
1563 n.InnerText = value;
1567 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1569 XmlElement n = parent.OwnerDocument.CreateElement (element);
1570 parent.AppendChild (n);
1571 n.SetAttribute (attribute, value);
1575 internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
1577 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1578 dest.AppendChild (copy);
1582 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1583 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1586 node = WriteElement(parent, element);
1587 node.InnerText = value;
1589 private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
1590 XmlElement node = WriteElement(parent, element);
1591 if (node.GetAttribute(attribute) == value) return;
1592 node.SetAttribute(attribute, value);
1594 internal static void ClearElement(XmlElement parent, string name) {
1595 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1597 parent.RemoveChild(node);
1600 // DOCUMENTATION HELPER FUNCTIONS
1602 private void MakeDocNode (DocsNodeInfo info)
1604 List<GenericParameter> genericParams = info.GenericParameters;
1605 IList<ParameterDefinition> parameters = info.Parameters;
1606 TypeReference returntype = info.ReturnType;
1607 bool returnisreturn = info.ReturnIsReturn;
1608 XmlElement e = info.Node;
1609 bool addremarks = info.AddRemarks;
1611 WriteElementInitialText(e, "summary", "To be added.");
1613 if (parameters != null) {
1614 string[] values = new string [parameters.Count];
1615 for (int i = 0; i < values.Length; ++i)
1616 values [i] = parameters [i].Name;
1617 UpdateParameters (e, "param", values);
1620 if (genericParams != null) {
1621 string[] values = new string [genericParams.Count];
1622 for (int i = 0; i < values.Length; ++i)
1623 values [i] = genericParams [i].Name;
1624 UpdateParameters (e, "typeparam", values);
1627 string retnodename = null;
1628 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
1629 retnodename = returnisreturn ? "returns" : "value";
1630 string retnodename_other = !returnisreturn ? "returns" : "value";
1632 // If it has a returns node instead of a value node, change its name.
1633 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1634 if (retother != null) {
1635 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1636 foreach (XmlNode node in retother)
1637 retnode.AppendChild(node.CloneNode(true));
1638 e.ReplaceChild(retnode, retother);
1640 WriteElementInitialText(e, retnodename, "To be added.");
1643 ClearElement(e, "returns");
1644 ClearElement(e, "value");
1648 WriteElementInitialText(e, "remarks", "To be added.");
1650 if (exceptions.HasValue && info.Member != null &&
1651 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
1652 UpdateExceptions (e, info.Member);
1655 foreach (DocumentationImporter importer in importers)
1656 importer.ImportDocumentation (info);
1658 OrderDocsNodes (e, e.ChildNodes);
1659 NormalizeWhitespace(e);
1662 static readonly string[] DocsNodeOrder = {
1663 "typeparam", "param", "summary", "returns", "value", "remarks",
1666 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
1668 ReorderNodes (docs, children, DocsNodeOrder);
1672 private void UpdateParameters (XmlElement e, string element, string[] values)
1674 if (values != null) {
1675 XmlNode[] paramnodes = new XmlNode[values.Length];
1677 // Some documentation had param nodes with leading spaces.
1678 foreach (XmlElement paramnode in e.SelectNodes(element)){
1679 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
1682 // If a member has only one parameter, we can track changes to
1683 // the name of the parameter easily.
1684 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
1685 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
1688 bool reinsert = false;
1690 // Pick out existing and still-valid param nodes, and
1691 // create nodes for parameters not in the file.
1692 Hashtable seenParams = new Hashtable();
1693 for (int pi = 0; pi < values.Length; pi++) {
1694 string p = values [pi];
1697 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
1698 if (paramnodes[pi] != null) continue;
1700 XmlElement pe = e.OwnerDocument.CreateElement(element);
1701 pe.SetAttribute("name", p);
1702 pe.InnerText = "To be added.";
1703 paramnodes[pi] = pe;
1707 // Remove parameters that no longer exist and check all params are in the right order.
1709 MyXmlNodeList todelete = new MyXmlNodeList ();
1710 foreach (XmlElement paramnode in e.SelectNodes(element)) {
1711 string name = paramnode.GetAttribute("name");
1712 if (!seenParams.ContainsKey(name)) {
1713 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1714 Warning ("The following param node can only be deleted if the --delete option is given: ");
1715 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
1717 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
1718 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1722 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
1723 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1724 e.ParentNode.Attributes ["MemberName"].Value,
1727 Warning ("\tValue={0}", paramnode.OuterXml);
1729 todelete.Add (paramnode);
1734 if ((int)seenParams[name] != idx)
1740 foreach (XmlNode n in todelete) {
1741 n.ParentNode.RemoveChild (n);
1744 // Re-insert the parameter nodes at the top of the doc section.
1746 for (int pi = values.Length-1; pi >= 0; pi--)
1747 e.PrependChild(paramnodes[pi]);
1749 // Clear all existing param nodes
1750 foreach (XmlNode paramnode in e.SelectNodes(element)) {
1751 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1752 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
1753 Console.WriteLine(paramnode.OuterXml);
1755 paramnode.ParentNode.RemoveChild(paramnode);
1761 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
1763 string existingName = pe.GetAttribute ("name");
1764 pe.SetAttribute ("name", newName);
1765 if (existingName == newName)
1767 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
1768 if (paramref.GetAttribute ("name").Trim () == existingName)
1769 paramref.SetAttribute ("name", newName);
1772 class CrefComparer : XmlNodeComparer {
1774 public CrefComparer ()
1778 public override int Compare (XmlNode x, XmlNode y)
1780 string xType = x.Attributes ["cref"].Value;
1781 string yType = y.Attributes ["cref"].Value;
1782 string xNamespace = GetNamespace (xType);
1783 string yNamespace = GetNamespace (yType);
1785 int c = xNamespace.CompareTo (yNamespace);
1788 return xType.CompareTo (yType);
1791 static string GetNamespace (string type)
1793 int n = type.LastIndexOf ('.');
1795 return type.Substring (0, n);
1796 return string.Empty;
1800 private void UpdateExceptions (XmlNode docs, MemberReference member)
1802 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
1803 string cref = slashdocFormatter.GetDeclaration (source.Exception);
1804 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
1807 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
1808 e.SetAttribute ("cref", cref);
1809 e.InnerXml = "To be added; from: <see cref=\"" +
1810 string.Join ("\" />, <see cref=\"",
1811 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
1814 docs.AppendChild (e);
1816 SortXmlNodes (docs, docs.SelectNodes ("exception"),
1817 new CrefComparer ());
1820 private static void NormalizeWhitespace(XmlElement e) {
1821 // Remove all text and whitespace nodes from the element so it
1822 // is outputted with nice indentation and no blank lines.
1823 ArrayList deleteNodes = new ArrayList();
1824 foreach (XmlNode n in e)
1825 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
1827 foreach (XmlNode n in deleteNodes)
1828 n.ParentNode.RemoveChild(n);
1831 private static bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
1833 TypeDefinition type = member as TypeDefinition;
1835 type = member.DeclaringType as TypeDefinition;
1836 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
1839 private static string GetAssemblyVersion (AssemblyDefinition assembly)
1841 return assembly.Name.Version.ToString();
1844 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
1846 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
1848 e = root.OwnerDocument.CreateElement("AssemblyInfo");
1849 root.AppendChild(e);
1851 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
1852 .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
1854 // matches.Count > 0 && add: ignore -- already present
1855 if (matches.Count > 0 && !add) {
1856 foreach (XmlNode c in matches)
1859 else if (matches.Count == 0 && add) {
1860 foreach (string sv in assemblyVersions) {
1861 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
1866 // matches.Count == 0 && !add: ignore -- already not present
1868 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
1869 SortXmlNodes (e, avs, new VersionComparer ());
1871 return avs.Count != 0;
1874 // FIXME: get TypeReferences instead of string comparison?
1875 private static string[] IgnorableAttributes = {
1876 // Security related attributes
1877 "System.Reflection.AssemblyKeyFileAttribute",
1878 "System.Reflection.AssemblyDelaySignAttribute",
1879 // Present in @RefType
1880 "System.Runtime.InteropServices.OutAttribute",
1881 // For naming the indexer to use when not using indexers
1882 "System.Reflection.DefaultMemberAttribute",
1883 // for decimal constants
1884 "System.Runtime.CompilerServices.DecimalConstantAttribute",
1885 // compiler generated code
1886 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
1887 // more compiler generated code, e.g. iterator methods
1888 "System.Diagnostics.DebuggerHiddenAttribute",
1889 "System.Runtime.CompilerServices.FixedBufferAttribute",
1890 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
1891 // extension methods
1892 "System.Runtime.CompilerServices.ExtensionAttribute",
1893 // Used to differentiate 'object' from C#4 'dynamic'
1894 "System.Runtime.CompilerServices.DynamicAttribute",
1897 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes)
1899 if (!attributes.Any ()) {
1900 ClearElement (root, "Attributes");
1904 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
1908 e = root.OwnerDocument.CreateElement("Attributes");
1910 foreach (string attribute in attributes) {
1911 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
1914 WriteElementText(ae, "AttributeName", attribute);
1917 if (e.ParentNode == null)
1918 root.AppendChild(e);
1920 NormalizeWhitespace(e);
1923 private static string MakeAttributesValueString (object v, TypeReference valueType)
1927 if (valueType.FullName == "System.Type")
1928 return "typeof(" + v.ToString () + ")";
1929 if (valueType.FullName == "System.String")
1930 return "\"" + v.ToString () + "\"";
1932 return (bool)v ? "true" : "false";
1933 TypeDefinition valueDef = valueType.Resolve ();
1934 if (valueDef == null || !valueDef.IsEnum)
1935 return v.ToString ();
1936 string typename = GetDocTypeFullName (valueType);
1937 var values = GetEnumerationValues (valueDef);
1938 long c = ToInt64 (v);
1939 if (values.ContainsKey (c))
1940 return typename + "." + values [c];
1941 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
1942 return string.Join (" | ",
1943 (from i in values.Keys
1945 select typename + "." + values [i])
1948 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
1951 private static Dictionary<long, string> GetEnumerationValues (TypeDefinition type)
1953 var values = new Dictionary<long, string> ();
1955 (from f in type.Fields
1956 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
1958 values [ToInt64 (f.Constant)] = f.Name;
1963 static long ToInt64 (object value)
1966 return (long) (ulong) value;
1967 return Convert.ToInt64 (value);
1970 private void MakeParameters (XmlElement root, IList<ParameterDefinition> parameters)
1972 XmlElement e = WriteElement(root, "Parameters");
1974 foreach (ParameterDefinition p in parameters) {
1975 XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
1977 pe.SetAttribute("Name", p.Name);
1978 pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
1979 if (p.ParameterType is ByReferenceType) {
1980 if (p.IsOut) pe.SetAttribute("RefType", "out");
1981 else pe.SetAttribute("RefType", "ref");
1983 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
1987 private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams)
1989 if (typeParams == null || typeParams.Count == 0) {
1990 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
1992 root.RemoveChild (f);
1995 XmlElement e = WriteElement(root, "TypeParameters");
1997 foreach (GenericParameter t in typeParams) {
1998 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2000 pe.SetAttribute("Name", t.Name);
2001 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""));
2002 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2003 IList<TypeReference> constraints = t.Constraints;
2004 GenericParameterAttributes attrs = t.Attributes;
2005 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2013 ce = root.OwnerDocument.CreateElement ("Constraints");
2015 pe.AppendChild (ce);
2016 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2017 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2018 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2019 AppendElementText (ce, "ParameterAttribute", "Covariant");
2020 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2021 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2022 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2023 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2024 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2025 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2026 foreach (TypeReference c in constraints) {
2027 TypeDefinition cd = c.Resolve ();
2028 AppendElementText (ce,
2029 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2030 GetDocTypeFullName (c));
2035 private void MakeParameters (XmlElement root, MemberReference mi)
2037 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2038 MakeParameters (root, ((MethodDefinition)mi).Parameters);
2039 else if (mi is MethodDefinition) {
2040 MethodDefinition mb = (MethodDefinition) mi;
2041 IList<ParameterDefinition> parameters = mb.Parameters;
2042 MakeParameters(root, parameters);
2043 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2044 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2045 p.SetAttribute ("RefType", "this");
2048 else if (mi is PropertyDefinition) {
2049 IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
2050 if (parameters.Count > 0)
2051 MakeParameters(root, parameters);
2055 else if (mi is FieldDefinition) return;
2056 else if (mi is EventDefinition) return;
2057 else throw new ArgumentException();
2060 internal static string GetDocParameterType (TypeReference type)
2062 return GetDocTypeFullName (type).Replace ("@", "&");
2065 private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes)
2067 XmlElement e = WriteElement(root, "ReturnValue");
2069 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2070 if (attributes != null)
2071 MakeAttributes(e, GetCustomAttributes (attributes, ""));
2074 private void MakeReturnValue (XmlElement root, MemberReference mi)
2076 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2078 else if (mi is MethodDefinition)
2079 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes);
2080 else if (mi is PropertyDefinition)
2081 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
2082 else if (mi is FieldDefinition)
2083 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
2084 else if (mi is EventDefinition)
2085 MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
2087 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2090 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2092 MemberReference mi = info.Member;
2093 if (mi is TypeDefinition) return null;
2095 string sigs = memberFormatters [0].GetDeclaration (mi);
2096 if (sigs == null) return null; // not publicly visible
2098 // no documentation for property/event accessors. Is there a better way of doing this?
2099 if (mi.Name.StartsWith("get_")) return null;
2100 if (mi.Name.StartsWith("set_")) return null;
2101 if (mi.Name.StartsWith("add_")) return null;
2102 if (mi.Name.StartsWith("remove_")) return null;
2103 if (mi.Name.StartsWith("raise_")) return null;
2105 XmlElement me = doc.CreateElement("Member");
2106 me.SetAttribute("MemberName", GetMemberName (mi));
2110 if (exceptions.HasValue &&
2111 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2112 UpdateExceptions (info.Node, info.Member);
2114 if (since != null) {
2115 XmlNode docs = me.SelectSingleNode("Docs");
2116 docs.AppendChild (CreateSinceNode (doc));
2122 internal static string GetMemberName (MemberReference mi)
2124 MethodDefinition mb = mi as MethodDefinition;
2126 PropertyDefinition pi = mi as PropertyDefinition;
2129 return DocUtils.GetPropertyName (pi);
2131 StringBuilder sb = new StringBuilder (mi.Name.Length);
2132 if (!DocUtils.IsExplicitlyImplemented (mb))
2133 sb.Append (mi.Name);
2135 TypeReference iface;
2136 MethodReference ifaceMethod;
2137 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2138 sb.Append (GetDocTypeFullName (iface));
2140 sb.Append (ifaceMethod.Name);
2142 if (mb.IsGenericMethod ()) {
2143 IList<GenericParameter> typeParams = mb.GenericParameters;
2144 if (typeParams.Count > 0) {
2146 sb.Append (typeParams [0].Name);
2147 for (int i = 1; i < typeParams.Count; ++i)
2148 sb.Append (",").Append (typeParams [i].Name);
2152 return sb.ToString ();
2155 /// SIGNATURE GENERATION FUNCTIONS
2156 internal static bool IsPrivate (MemberReference mi)
2158 return memberFormatters [0].GetDeclaration (mi) == null;
2161 internal static string GetMemberType (MemberReference mi)
2163 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2164 return "Constructor";
2165 if (mi is MethodDefinition)
2167 if (mi is PropertyDefinition)
2169 if (mi is FieldDefinition)
2171 if (mi is EventDefinition)
2173 throw new ArgumentException();
2176 private static string GetDocTypeName (TypeReference type)
2178 return docTypeFormatter.GetName (type);
2181 internal static string GetDocTypeFullName (TypeReference type)
2183 return DocTypeFullMemberFormatter.Default.GetName (type);
2186 internal static string GetXPathForMember (DocumentationMember member)
2188 StringBuilder xpath = new StringBuilder ();
2189 xpath.Append ("//Members/Member[@MemberName=\"")
2190 .Append (member.MemberName)
2192 if (member.Parameters != null && member.Parameters.Count > 0) {
2193 xpath.Append ("/Parameters[count(Parameter) = ")
2194 .Append (member.Parameters.Count);
2195 for (int i = 0; i < member.Parameters.Count; ++i) {
2196 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2197 xpath.Append (member.Parameters [i]);
2198 xpath.Append ("\"");
2200 xpath.Append ("]/..");
2202 return xpath.ToString ();
2205 public static string GetXPathForMember (XPathNavigator member)
2207 StringBuilder xpath = new StringBuilder ();
2208 xpath.Append ("//Type[@FullName=\"")
2209 .Append (member.SelectSingleNode ("../../@FullName").Value)
2211 xpath.Append ("Members/Member[@MemberName=\"")
2212 .Append (member.SelectSingleNode ("@MemberName").Value)
2214 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2215 if (parameters.Count > 0) {
2216 xpath.Append ("/Parameters[count(Parameter) = ")
2217 .Append (parameters.Count);
2219 while (parameters.MoveNext ()) {
2221 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2222 xpath.Append (parameters.Current.Value);
2223 xpath.Append ("\"");
2225 xpath.Append ("]/..");
2227 return xpath.ToString ();
2230 public static string GetXPathForMember (MemberReference member)
2232 StringBuilder xpath = new StringBuilder ();
2233 xpath.Append ("//Type[@FullName=\"")
2234 .Append (member.DeclaringType.FullName)
2236 xpath.Append ("Members/Member[@MemberName=\"")
2237 .Append (GetMemberName (member))
2240 IList<ParameterDefinition> parameters = null;
2241 if (member is MethodDefinition)
2242 parameters = ((MethodDefinition) member).Parameters;
2243 else if (member is PropertyDefinition) {
2244 parameters = ((PropertyDefinition) member).Parameters;
2246 if (parameters != null && parameters.Count > 0) {
2247 xpath.Append ("/Parameters[count(Parameter) = ")
2248 .Append (parameters.Count);
2249 for (int i = 0; i < parameters.Count; ++i) {
2250 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2251 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2252 xpath.Append ("\"");
2254 xpath.Append ("]/..");
2256 return xpath.ToString ();
2260 static class CecilExtensions {
2261 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
2263 foreach (var c in type.Methods.Where (m => m.IsConstructor))
2264 yield return (MemberReference) c;
2265 foreach (var e in type.Events)
2266 yield return (MemberReference) e;
2267 foreach (var f in type.Fields)
2268 yield return (MemberReference) f;
2269 foreach (var m in type.Methods.Where (m => !m.IsConstructor))
2270 yield return (MemberReference) m;
2271 foreach (var t in type.NestedTypes)
2272 yield return (MemberReference) t;
2273 foreach (var p in type.Properties)
2274 yield return (MemberReference) p;
2277 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
2279 return GetMembers (type).Where (m => m.Name == member);
2282 public static MemberReference GetMember (this TypeDefinition type, string member)
2284 return GetMembers (type, member).EnsureZeroOrOne ();
2287 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2289 if (source.Count () > 1)
2290 throw new InvalidOperationException ("too many matches");
2291 return source.FirstOrDefault ();
2294 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2297 .Where (m => m.Name == method)
2298 .EnsureZeroOrOne ();
2301 public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
2303 TypeDefinition def = type as TypeDefinition;
2305 return new MemberReference [0];
2306 CustomAttribute defMemberAttr = def.CustomAttributes
2307 .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
2308 if (defMemberAttr == null)
2309 return new MemberReference [0];
2310 string name = (string) defMemberAttr.ConstructorArguments [0].Value;
2311 return def.Properties
2312 .Where (p => p.Name == name)
2313 .Select (p => (MemberReference) p);
2316 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2318 return assembly.Modules.SelectMany (md => md.GetAllTypes ());
2321 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2323 return GetTypes (assembly)
2324 .Where (td => td.FullName == type)
2325 .EnsureZeroOrOne ();
2328 public static bool IsGenericType (this TypeReference type)
2330 return type.GenericParameters.Count > 0;
2333 public static bool IsGenericMethod (this MethodReference method)
2335 return method.GenericParameters.Count > 0;
2338 public static MemberReference Resolve (this MemberReference member)
2340 FieldReference fr = member as FieldReference;
2342 return fr.Resolve ();
2343 MethodReference mr = member as MethodReference;
2345 return mr.Resolve ();
2346 TypeReference tr = member as TypeReference;
2348 return tr.Resolve ();
2349 PropertyReference pr = member as PropertyReference;
2352 EventReference er = member as EventReference;
2355 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2358 public static TypeReference GetUnderlyingType (this TypeDefinition type)
2362 return type.Fields.First (f => f.Name == "value__").FieldType;
2365 public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
2367 return self.Types.SelectMany (t => t.GetAllTypes ());
2370 static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
2374 if (!self.HasNestedTypes)
2377 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
2382 static class DocUtils {
2383 public static bool IsExplicitlyImplemented (MethodDefinition method)
2385 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2388 public static string GetTypeDotMember (string name)
2390 int startType, startMethod;
2391 startType = startMethod = -1;
2392 for (int i = 0; i < name.Length; ++i) {
2393 if (name [i] == '.') {
2394 startType = startMethod;
2398 return name.Substring (startType+1);
2401 public static string GetMember (string name)
2403 int i = name.LastIndexOf ('.');
2406 return name.Substring (i+1);
2409 public static void GetInfoForExplicitlyImplementedMethod (
2410 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2414 if (method.Overrides.Count != 1)
2415 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2416 iface = method.Overrides [0].DeclaringType;
2417 ifaceMethod = method.Overrides [0];
2420 public static string GetPropertyName (PropertyDefinition pi)
2422 // Issue: (g)mcs-generated assemblies that explicitly implement
2423 // properties don't specify the full namespace, just the
2424 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2425 MethodDefinition method = pi.GetMethod;
2427 method = pi.SetMethod;
2428 if (!IsExplicitlyImplemented (method))
2431 // Need to determine appropriate namespace for this member.
2432 TypeReference iface;
2433 MethodReference ifaceMethod;
2434 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2435 return string.Join (".", new string[]{
2436 DocTypeFullMemberFormatter.Default.GetName (iface),
2437 GetMember (pi.Name)});
2440 public static string GetNamespace (TypeReference type)
2442 if (type.GetElementType ().IsNested)
2443 type = type.GetElementType ();
2444 while (type != null && type.IsNested)
2445 type = type.DeclaringType;
2447 return string.Empty;
2448 return type.Namespace;
2451 public static string PathCombine (string dir, string path)
2457 return Path.Combine (dir, path);
2460 public static bool IsExtensionMethod (MethodDefinition method)
2463 method.CustomAttributes
2464 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2465 && method.DeclaringType.CustomAttributes
2466 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
2469 public static bool IsDelegate (TypeDefinition type)
2471 TypeReference baseRef = type.BaseType;
2472 if (baseRef == null)
2474 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2475 baseRef.FullName == "System.MulticastDelegate";
2478 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2480 List<TypeReference> decls = new List<TypeReference> ();
2482 while (type.DeclaringType != null) {
2483 decls.Add (type.DeclaringType);
2484 type = type.DeclaringType;
2490 public static int GetGenericArgumentCount (TypeReference type)
2492 GenericInstanceType inst = type as GenericInstanceType;
2494 ? inst.GenericArguments.Count
2495 : type.GenericParameters.Count;
2498 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
2500 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
2501 List<TypeReference> userInterfaces = new List<TypeReference> ();
2502 foreach (TypeReference iface in type.Interfaces) {
2503 TypeReference lookup = iface.Resolve () ?? iface;
2504 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
2505 userInterfaces.Add (iface);
2507 return userInterfaces;
2510 private static string GetQualifiedTypeName (TypeReference type)
2512 return "[" + type.Scope.Name + "]" + type.FullName;
2515 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
2517 HashSet<string> inheritedInterfaces = new HashSet<string> ();
2518 Action<TypeDefinition> a = null;
2520 if (t == null) return;
2521 foreach (TypeReference r in t.Interfaces) {
2522 inheritedInterfaces.Add (GetQualifiedTypeName (r));
2526 TypeReference baseRef = type.BaseType;
2527 while (baseRef != null) {
2528 TypeDefinition baseDef = baseRef.Resolve ();
2529 if (baseDef != null) {
2531 baseRef = baseDef.BaseType;
2536 foreach (TypeReference r in type.Interfaces)
2538 return inheritedInterfaces;
2542 class DocsNodeInfo {
2543 public DocsNodeInfo (XmlElement node)
2548 public DocsNodeInfo (XmlElement node, TypeDefinition type)
2554 public DocsNodeInfo (XmlElement node, MemberReference member)
2557 SetMemberInfo (member);
2560 void SetType (TypeDefinition type)
2563 throw new ArgumentNullException ("type");
2565 GenericParameters = new List<GenericParameter> (type.GenericParameters);
2566 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
2567 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
2568 for (int i = 0; i < declTypes.Count - 1; ++i) {
2569 int remove = System.Math.Min (maxGenArgs,
2570 DocUtils.GetGenericArgumentCount (declTypes [i]));
2571 maxGenArgs -= remove;
2572 while (remove-- > 0)
2573 GenericParameters.RemoveAt (0);
2575 if (DocUtils.IsDelegate (type)) {
2576 Parameters = type.GetMethod("Invoke").Parameters;
2577 ReturnType = type.GetMethod("Invoke").ReturnType;
2578 ReturnIsReturn = true;
2582 void SetMemberInfo (MemberReference member)
2585 throw new ArgumentNullException ("member");
2586 ReturnIsReturn = true;
2590 if (member is MethodReference ) {
2591 MethodReference mr = (MethodReference) member;
2592 Parameters = mr.Parameters;
2593 if (mr.IsGenericMethod ()) {
2594 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
2597 else if (member is PropertyDefinition) {
2598 Parameters = ((PropertyDefinition) member).Parameters;
2601 if (member is MethodDefinition) {
2602 ReturnType = ((MethodDefinition) member).ReturnType;
2603 } else if (member is PropertyDefinition) {
2604 ReturnType = ((PropertyDefinition) member).PropertyType;
2605 ReturnIsReturn = false;
2608 // no remarks section for enum members
2609 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
2613 public TypeReference ReturnType;
2614 public List<GenericParameter> GenericParameters;
2615 public IList<ParameterDefinition> Parameters;
2616 public bool ReturnIsReturn;
2617 public XmlElement Node;
2618 public bool AddRemarks = true;
2619 public MemberReference Member;
2620 public TypeDefinition Type;
2623 class DocumentationEnumerator {
2625 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2627 return GetDocumentationTypes (assembly, forTypes, null);
2630 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2632 foreach (TypeDefinition type in assembly.GetTypes()) {
2633 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
2635 if (seen != null && seen.Contains (type.FullName))
2638 foreach (TypeDefinition nested in type.NestedTypes)
2639 yield return nested;
2643 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2645 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
2646 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
2647 oldmember.RemoveAttribute ("__monodocer-seen__");
2650 MemberReference m = GetMember (type, new DocumentationMember (oldmember));
2652 yield return new DocsNodeInfo (oldmember);
2655 yield return new DocsNodeInfo (oldmember, m);
2660 protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
2662 string membertype = member.MemberType;
2664 string returntype = member.ReturnType;
2666 string docName = member.MemberName;
2667 string[] docTypeParams = GetTypeParameters (docName);
2669 // Loop through all members in this type with the same name
2670 foreach (MemberReference mi in GetReflectionMembers (type, docName)) {
2671 if (mi is TypeDefinition) continue;
2672 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
2674 if (MDocUpdater.IsPrivate (mi))
2677 IList<ParameterDefinition> pis = null;
2678 string[] typeParams = null;
2679 if (mi is MethodDefinition) {
2680 MethodDefinition mb = (MethodDefinition) mi;
2681 pis = mb.Parameters;
2682 if (docTypeParams != null && mb.IsGenericMethod ()) {
2683 IList<GenericParameter> args = mb.GenericParameters;
2684 if (args.Count == docTypeParams.Length) {
2685 typeParams = args.Select (p => p.Name).ToArray ();
2689 else if (mi is PropertyDefinition)
2690 pis = ((PropertyDefinition)mi).Parameters;
2692 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
2693 int pcount = pis == null ? 0 : pis.Count;
2694 if (mcount != pcount)
2697 MethodDefinition mDef = mi as MethodDefinition;
2698 if (mDef != null && !mDef.IsConstructor) {
2699 // Casting operators can overload based on return type.
2700 if (returntype != GetReplacedString (
2701 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
2702 typeParams, docTypeParams)) {
2710 for (int i = 0; i < pis.Count; i++) {
2711 string paramType = GetReplacedString (
2712 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
2713 typeParams, docTypeParams);
2714 if (paramType != (string) member.Parameters [i]) {
2719 if (!good) continue;
2727 static string[] GetTypeParameters (string docName)
2729 if (docName [docName.Length-1] != '>')
2731 StringList types = new StringList ();
2732 int endToken = docName.Length-2;
2733 int i = docName.Length-2;
2735 if (docName [i] == ',' || docName [i] == '<') {
2736 types.Add (docName.Substring (i + 1, endToken - i));
2739 if (docName [i] == '<')
2744 return types.ToArray ();
2747 protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
2749 // need to worry about 4 forms of //@MemberName values:
2750 // 1. "Normal" (non-generic) member names: GetEnumerator
2752 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
2753 // - try as-is, and try type.member (due to "kludge" for property
2755 // 3. "Normal" Generic member names: Sort<T> (CSC)
2756 // - need to remove generic parameters --> "Sort"
2757 // 4. Explicitly-implemented interface members for generic interfaces:
2758 // -- System.Collections.Generic.IEnumerable<T>.Current
2759 // - Try as-is, and try type.member, *keeping* the generic parameters.
2760 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
2761 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
2762 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
2763 // this as (1) or (2).
2764 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
2766 foreach (MemberReference mi in type.GetMembers (docName))
2768 if (CountChars (docName, '.') > 0)
2769 // might be a property; try only type.member instead of
2770 // namespace.type.member.
2771 foreach (MemberReference mi in
2772 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
2779 int startLt, startType, startMethod;
2780 startLt = startType = startMethod = -1;
2781 for (int i = 0; i < docName.Length; ++i) {
2782 switch (docName [i]) {
2791 if (numLt == 0 && (i + 1) < docName.Length)
2792 // there's another character in docName, so this <...> sequence is
2793 // probably part of a generic type -- case 4.
2797 startType = startMethod;
2803 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
2805 foreach (MemberReference mi in type.GetMembers (refName))
2809 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
2812 // If we _still_ haven't found it, we've hit another generic naming issue:
2813 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
2814 // explicitly-implemented METHOD names (not properties), e.g.
2815 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
2816 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
2817 // which the XML docs will contain.
2819 // Alas, we can't derive the Mono name from docName, so we need to iterate
2820 // over all member names, convert them into CSC format, and compare... :-(
2823 foreach (MemberReference mi in type.GetMembers ()) {
2824 if (MDocUpdater.GetMemberName (mi) == docName)
2829 static string GetReplacedString (string typeName, string[] from, string[] to)
2833 for (int i = 0; i < from.Length; ++i)
2834 typeName = typeName.Replace (from [i], to [i]);
2838 private static int CountChars (string s, char c)
2841 for (int i = 0; i < s.Length; ++i) {
2849 class EcmaDocumentationEnumerator : DocumentationEnumerator {
2854 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
2857 this.ecmadocs = ecmaDocs;
2860 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2862 HashSet<string> seen = new HashSet<string> ();
2863 return GetDocumentationTypes (assembly, forTypes, seen)
2864 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
2867 new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2870 while (ecmadocs.Read ()) {
2871 switch (ecmadocs.Name) {
2873 if (typeDepth == -1)
2874 typeDepth = ecmadocs.Depth;
2875 if (ecmadocs.NodeType != XmlNodeType.Element)
2877 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
2879 string typename = ecmadocs.GetAttribute ("FullName");
2880 string typename2 = MDocUpdater.GetTypeFileName (typename);
2881 if (forTypes != null &&
2882 forTypes.BinarySearch (typename) < 0 &&
2883 typename != typename2 &&
2884 forTypes.BinarySearch (typename2) < 0)
2887 if ((t = assembly.GetType (typename)) == null &&
2888 (t = assembly.GetType (typename2)) == null)
2890 seen.Add (typename);
2891 if (typename != typename2)
2892 seen.Add (typename2);
2893 Console.WriteLine (" Import: {0}", t.FullName);
2894 if (ecmadocs.Name != "Docs") {
2895 int depth = ecmadocs.Depth;
2896 while (ecmadocs.Read ()) {
2897 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
2901 if (!ecmadocs.IsStartElement ("Docs"))
2902 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
2912 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2914 return GetMembers (basefile, type)
2915 .Concat (base.GetDocumentationMembers (basefile, type));
2918 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
2920 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
2923 if (ecmadocs.IsEmptyElement)
2926 int membersDepth = ecmadocs.Depth;
2928 while (go && ecmadocs.Read ()) {
2929 switch (ecmadocs.Name) {
2931 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
2933 DocumentationMember dm = new DocumentationMember (ecmadocs);
2934 string xp = MDocUpdater.GetXPathForMember (dm);
2935 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
2937 if (oldmember == null) {
2938 m = GetMember (type, dm);
2940 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2941 type.FullName, dm.MemberSignatures ["C#"]);
2942 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
2945 // oldmember lookup may have failed due to type parameter renames.
2947 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
2948 if (oldmember == null) {
2949 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
2950 oldmember = basefile.CreateElement ("Member");
2951 oldmember.SetAttribute ("MemberName", dm.MemberName);
2952 members.AppendChild (oldmember);
2953 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
2954 XmlElement ms = basefile.CreateElement ("MemberSignature");
2955 ms.SetAttribute ("Language", key);
2956 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
2957 oldmember.AppendChild (ms);
2959 oldmember.SetAttribute ("__monodocer-seen__", "true");
2960 Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
2965 m = GetMember (type, new DocumentationMember (oldmember));
2967 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2968 type.FullName, dm.MemberSignatures ["C#"]);
2971 oldmember.SetAttribute ("__monodocer-seen__", "true");
2973 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
2974 if (ecmadocs.Name != "Docs")
2975 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
2980 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
2989 abstract class DocumentationImporter {
2991 public abstract void ImportDocumentation (DocsNodeInfo info);
2994 class MsxdocDocumentationImporter : DocumentationImporter {
2996 XmlDocument slashdocs;
2998 public MsxdocDocumentationImporter (string file)
3000 var xml = File.ReadAllText (file);
3002 // Ensure Unix line endings
3003 xml = xml.Replace ("\r", "");
3005 slashdocs = new XmlDocument();
3006 slashdocs.LoadXml (xml);
3009 public override void ImportDocumentation (DocsNodeInfo info)
3011 XmlNode elem = GetDocs (info.Member ?? info.Type);
3016 XmlElement e = info.Node;
3018 if (elem.SelectSingleNode("summary") != null)
3019 MDocUpdater.ClearElement(e, "summary");
3020 if (elem.SelectSingleNode("remarks") != null)
3021 MDocUpdater.ClearElement(e, "remarks");
3022 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3023 MDocUpdater.ClearElement(e, "value");
3024 MDocUpdater.ClearElement(e, "returns");
3027 foreach (XmlNode child in elem.ChildNodes) {
3028 switch (child.Name) {
3031 XmlAttribute name = child.Attributes ["name"];
3034 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3036 p2.InnerXml = child.InnerXml;
3039 // Occasionally XML documentation will use <returns/> on
3040 // properties, so let's try to normalize things.
3043 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3044 v.InnerXml = child.InnerXml;
3050 case "permission": {
3051 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3054 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3056 a = e.OwnerDocument.CreateElement (child.Name);
3057 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
3060 a.InnerXml = child.InnerXml;
3064 XmlAttribute cref = child.Attributes ["cref"];
3067 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3069 a = e.OwnerDocument.CreateElement ("altmember");
3070 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
3077 if (child.NodeType == XmlNodeType.Element &&
3078 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3081 MDocUpdater.CopyNode (child, e);
3088 private XmlNode GetDocs (MemberReference member)
3090 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3091 if (slashdocsig != null)
3092 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3097 class EcmaDocumentationImporter : DocumentationImporter {
3101 public EcmaDocumentationImporter (XmlReader ecmaDocs)
3103 this.ecmadocs = ecmaDocs;
3106 public override void ImportDocumentation (DocsNodeInfo info)
3108 if (!ecmadocs.IsStartElement ("Docs")) {
3112 XmlElement e = info.Node;
3114 int depth = ecmadocs.Depth;
3115 ecmadocs.ReadStartElement ("Docs");
3116 while (ecmadocs.Read ()) {
3117 if (ecmadocs.Name == "Docs") {
3118 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3121 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3123 if (!ecmadocs.IsStartElement ())
3125 switch (ecmadocs.Name) {
3128 string name = ecmadocs.GetAttribute ("name");
3131 XmlNode doc = e.SelectSingleNode (
3132 ecmadocs.Name + "[@name='" + name + "']");
3133 string value = ecmadocs.ReadInnerXml ();
3135 doc.InnerXml = value.Replace ("\r", "");
3142 string name = ecmadocs.Name;
3143 string cref = ecmadocs.GetAttribute ("cref");
3146 XmlNode doc = e.SelectSingleNode (
3147 ecmadocs.Name + "[@cref='" + cref + "']");
3148 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3150 doc.InnerXml = value;
3152 XmlElement n = e.OwnerDocument.CreateElement (name);
3153 n.SetAttribute ("cref", cref);
3160 string name = ecmadocs.Name;
3161 string xpath = ecmadocs.Name;
3162 StringList attributes = new StringList (ecmadocs.AttributeCount);
3163 if (ecmadocs.MoveToFirstAttribute ()) {
3165 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3166 } while (ecmadocs.MoveToNextAttribute ());
3167 ecmadocs.MoveToContent ();
3169 if (attributes.Count > 0) {
3170 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3172 XmlNode doc = e.SelectSingleNode (xpath);
3173 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3175 doc.InnerXml = value;
3178 XmlElement n = e.OwnerDocument.CreateElement (name);
3180 foreach (string a in attributes) {
3181 int eq = a.IndexOf ('=');
3182 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3193 class DocumentationMember {
3194 public StringToStringMap MemberSignatures = new StringToStringMap ();
3195 public string ReturnType;
3196 public StringList Parameters;
3197 public string MemberName;
3198 public string MemberType;
3200 public DocumentationMember (XmlReader reader)
3202 MemberName = reader.GetAttribute ("MemberName");
3203 int depth = reader.Depth;
3205 StringList p = new StringList ();
3207 if (reader.NodeType != XmlNodeType.Element)
3209 switch (reader.Name) {
3210 case "MemberSignature":
3211 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3214 MemberType = reader.ReadElementString ();
3217 if (reader.Depth == depth + 2)
3218 ReturnType = reader.ReadElementString ();
3221 if (reader.Depth == depth + 2)
3222 p.Add (reader.GetAttribute ("Type"));
3225 if (reader.Depth == depth + 1)
3229 } while (go && reader.Read () && reader.Depth >= depth);
3235 public DocumentationMember (XmlNode node)
3237 MemberName = node.Attributes ["MemberName"].Value;
3238 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3239 XmlAttribute l = n.Attributes ["Language"];
3240 XmlAttribute v = n.Attributes ["Value"];
3241 if (l != null && v != null)
3242 MemberSignatures [l.Value] = v.Value;
3244 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3245 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
3247 ReturnType = rt.InnerText;
3248 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
3250 Parameters = new StringList (p.Count);
3251 for (int i = 0; i < p.Count; ++i)
3252 Parameters.Add (p [i].Attributes ["Type"].Value);
3257 public enum MemberFormatterState {
3260 WithinGenericTypeParameters,
3263 public abstract class MemberFormatter {
3265 public virtual string Language {
3269 public virtual string GetName (MemberReference member)
3271 TypeReference type = member as TypeReference;
3273 return GetTypeName (type);
3274 MethodReference method = member as MethodReference;
3275 if (method != null && method.Name == ".ctor") // method.IsConstructor
3276 return GetConstructorName (method);
3278 return GetMethodName (method);
3279 PropertyReference prop = member as PropertyReference;
3281 return GetPropertyName (prop);
3282 FieldReference field = member as FieldReference;
3284 return GetFieldName (field);
3285 EventReference e = member as EventReference;
3287 return GetEventName (e);
3288 throw new NotSupportedException ("Can't handle: " +
3289 (member == null ? "null" : member.GetType().ToString()));
3292 protected virtual string GetTypeName (TypeReference type)
3295 throw new ArgumentNullException ("type");
3296 return _AppendTypeName (new StringBuilder (type.Name.Length), type).ToString ();
3299 protected virtual char[] ArrayDelimeters {
3300 get {return new char[]{'[', ']'};}
3303 protected virtual MemberFormatterState MemberFormatterState { get; set; }
3305 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type)
3307 if (type is ArrayType) {
3308 TypeSpecification spec = type as TypeSpecification;
3309 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType ())
3310 .Append (ArrayDelimeters [0]);
3311 var origState = MemberFormatterState;
3312 MemberFormatterState = MemberFormatterState.WithinArray;
3313 ArrayType array = (ArrayType) type;
3314 int rank = array.Rank;
3316 buf.Append (new string (',', rank-1));
3317 MemberFormatterState = origState;
3318 return buf.Append (ArrayDelimeters [1]);
3320 if (type is ByReferenceType) {
3321 return AppendRefTypeName (buf, type);
3323 if (type is PointerType) {
3324 return AppendPointerTypeName (buf, type);
3326 AppendNamespace (buf, type);
3327 if (type is GenericParameter) {
3328 return AppendTypeName (buf, type);
3330 GenericInstanceType genInst = type as GenericInstanceType;
3331 if (type.GenericParameters.Count == 0 &&
3332 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3333 return AppendFullTypeName (buf, type);
3335 return AppendGenericType (buf, type);
3338 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3340 string ns = DocUtils.GetNamespace (type);
3341 if (ns != null && ns.Length > 0)
3342 buf.Append (ns).Append ('.');
3346 protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type)
3348 if (type.DeclaringType != null)
3349 AppendFullTypeName (buf, type.DeclaringType).Append (NestedTypeSeparator);
3350 return AppendTypeName (buf, type);
3353 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3355 return AppendTypeName (buf, type.Name);
3358 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3360 int n = typename.IndexOf ("`");
3362 return buf.Append (typename.Substring (0, n));
3363 return buf.Append (typename);
3366 protected virtual string RefTypeModifier {
3370 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type)
3372 TypeSpecification spec = type as TypeSpecification;
3373 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType ())
3374 .Append (RefTypeModifier);
3377 protected virtual string PointerModifier {
3381 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type)
3383 TypeSpecification spec = type as TypeSpecification;
3384 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType ())
3385 .Append (PointerModifier);
3388 protected virtual char[] GenericTypeContainer {
3389 get {return new char[]{'<', '>'};}
3392 protected virtual char NestedTypeSeparator {
3396 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3398 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3399 type is GenericInstanceType ? type.GetElementType () : type);
3400 List<TypeReference> genArgs = GetGenericArguments (type);
3403 bool insertNested = false;
3404 foreach (var decl in decls) {
3405 TypeReference declDef = decl.Resolve () ?? decl;
3407 buf.Append (NestedTypeSeparator);
3409 insertNested = true;
3410 AppendTypeName (buf, declDef);
3411 int ac = DocUtils.GetGenericArgumentCount (declDef);
3415 buf.Append (GenericTypeContainer [0]);
3416 var origState = MemberFormatterState;
3417 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3418 _AppendTypeName (buf, genArgs [argIdx++]);
3419 for (int i = 1; i < c; ++i)
3420 _AppendTypeName (buf.Append (","), genArgs [argIdx++]);
3421 MemberFormatterState = origState;
3422 buf.Append (GenericTypeContainer [1]);
3428 protected List<TypeReference> GetGenericArguments (TypeReference type)
3430 var args = new List<TypeReference> ();
3431 GenericInstanceType inst = type as GenericInstanceType;
3433 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
3435 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
3439 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3444 protected virtual string GetConstructorName (MethodReference constructor)
3446 return constructor.Name;
3449 protected virtual string GetMethodName (MethodReference method)
3454 protected virtual string GetPropertyName (PropertyReference property)
3456 return property.Name;
3459 protected virtual string GetFieldName (FieldReference field)
3464 protected virtual string GetEventName (EventReference e)
3469 public virtual string GetDeclaration (MemberReference member)
3472 throw new ArgumentNullException ("member");
3473 TypeDefinition type = member as TypeDefinition;
3475 return GetTypeDeclaration (type);
3476 MethodDefinition method = member as MethodDefinition;
3477 if (method != null && method.IsConstructor)
3478 return GetConstructorDeclaration (method);
3480 return GetMethodDeclaration (method);
3481 PropertyDefinition prop = member as PropertyDefinition;
3483 return GetPropertyDeclaration (prop);
3484 FieldDefinition field = member as FieldDefinition;
3486 return GetFieldDeclaration (field);
3487 EventDefinition e = member as EventDefinition;
3489 return GetEventDeclaration (e);
3490 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3493 protected virtual string GetTypeDeclaration (TypeDefinition type)
3496 throw new ArgumentNullException ("type");
3497 StringBuilder buf = new StringBuilder (type.Name.Length);
3498 _AppendTypeName (buf, type);
3499 AppendGenericTypeConstraints (buf, type);
3500 return buf.ToString ();
3503 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
3505 return GetConstructorName (constructor);
3508 protected virtual string GetMethodDeclaration (MethodDefinition method)
3510 // Special signature for destructors.
3511 if (method.Name == "Finalize" && method.Parameters.Count == 0)
3512 return GetFinalizerName (method);
3514 StringBuilder buf = new StringBuilder ();
3516 AppendVisibility (buf, method);
3517 if (buf.Length == 0 &&
3518 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3521 AppendModifiers (buf, method);
3523 if (buf.Length != 0)
3525 buf.Append (GetTypeName (method.MethodReturnType)).Append (" ");
3527 AppendMethodName (buf, method);
3528 AppendGenericMethod (buf, method).Append (" ");
3529 AppendParameters (buf, method, method.Parameters);
3530 AppendGenericMethodConstraints (buf, method);
3531 return buf.ToString ();
3534 protected virtual string GetTypeName (MethodReturnType returnType)
3536 return GetName (returnType.ReturnType);
3539 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3541 return buf.Append (method.Name);
3544 protected virtual string GetFinalizerName (MethodDefinition method)
3549 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3554 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3559 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3564 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
3569 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3574 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
3576 return GetPropertyName (property);
3579 protected virtual string GetFieldDeclaration (FieldDefinition field)
3581 return GetFieldName (field);
3584 protected virtual string GetEventDeclaration (EventDefinition e)
3586 return GetEventName (e);
3590 class ILFullMemberFormatter : MemberFormatter {
3592 public override string Language {
3593 get {return "ILAsm";}
3596 protected override char NestedTypeSeparator {
3602 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3604 if (GetBuiltinType (type.FullName) != null)
3606 string ns = DocUtils.GetNamespace (type);
3607 if (ns != null && ns.Length > 0) {
3608 if (type.IsValueType)
3609 buf.Append ("valuetype ");
3611 buf.Append ("class ");
3612 buf.Append (ns).Append ('.');
3617 private static string GetBuiltinType (string t)
3620 case "System.Byte": return "unsigned int8";
3621 case "System.SByte": return "int8";
3622 case "System.Int16": return "int16";
3623 case "System.Int32": return "int32";
3624 case "System.Int64": return "int64";
3625 case "System.IntPtr": return "native int";
3627 case "System.UInt16": return "unsigned int16";
3628 case "System.UInt32": return "unsigned int32";
3629 case "System.UInt64": return "unsigned int64";
3630 case "System.UIntPtr": return "native unsigned int";
3632 case "System.Single": return "float32";
3633 case "System.Double": return "float64";
3634 case "System.Boolean": return "bool";
3635 case "System.Char": return "char";
3636 case "System.Void": return "void";
3637 case "System.String": return "string";
3638 case "System.Object": return "object";
3643 protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
3645 return buf.Append (typename);
3648 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3650 if (type is GenericParameter)
3651 return AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
3653 string s = GetBuiltinType (type.FullName);
3655 return buf.Append (s);
3656 return base.AppendTypeName (buf, type);
3659 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
3661 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
3662 return buf.Append (type.Owner is TypeReference ? "!" : "!!");
3664 GenericParameterAttributes attrs = type.Attributes;
3665 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
3666 buf.Append ("class ");
3667 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
3668 buf.Append ("struct ");
3669 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
3670 buf.Append (".ctor ");
3671 IList<TypeReference> constraints = type.Constraints;
3672 MemberFormatterState = 0;
3673 if (constraints.Count > 0) {
3674 var full = new ILFullMemberFormatter ();
3675 buf.Append ("(").Append (full.GetName (constraints [0]));
3676 for (int i = 1; i < constraints.Count; ++i) {
3677 buf.Append (", ").Append (full.GetName (constraints [i]));
3681 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3683 if ((attrs & GenericParameterAttributes.Covariant) != 0)
3685 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
3690 protected override string GetTypeDeclaration (TypeDefinition type)
3692 string visibility = GetTypeVisibility (type.Attributes);
3693 if (visibility == null)
3696 StringBuilder buf = new StringBuilder ();
3698 buf.Append (".class ");
3700 buf.Append ("nested ");
3701 buf.Append (visibility).Append (" ");
3702 if (type.IsInterface)
3703 buf.Append ("interface ");
3704 if (type.IsSequentialLayout)
3705 buf.Append ("sequential ");
3706 if (type.IsAutoLayout)
3707 buf.Append ("auto ");
3708 if (type.IsAnsiClass)
3709 buf.Append ("ansi ");
3710 if (type.IsAbstract)
3711 buf.Append ("abstract ");
3712 if (type.IsSerializable)
3713 buf.Append ("serializable ");
3715 buf.Append ("sealed ");
3716 if (type.IsBeforeFieldInit)
3717 buf.Append ("beforefieldinit ");
3718 var state = MemberFormatterState;
3719 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3720 buf.Append (GetName (type));
3721 MemberFormatterState = state;
3722 var full = new ILFullMemberFormatter ();
3723 if (type.BaseType != null) {
3724 buf.Append (" extends ");
3725 if (type.BaseType.FullName == "System.Object")
3726 buf.Append ("System.Object");
3728 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
3731 foreach (var name in type.Interfaces
3732 .Select (i => full.GetName (i))
3733 .OrderBy (n => n)) {
3735 buf.Append (" implements ");
3744 return buf.ToString ();
3747 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3749 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3750 type is GenericInstanceType ? type.GetElementType () : type);
3752 foreach (var decl in decls) {
3753 TypeReference declDef = decl.Resolve () ?? decl;
3755 buf.Append (NestedTypeSeparator);
3758 AppendTypeName (buf, declDef);
3762 foreach (TypeReference arg in GetGenericArguments (type)) {
3766 _AppendTypeName (buf, arg);
3772 static string GetTypeVisibility (TypeAttributes ta)
3774 switch (ta & TypeAttributes.VisibilityMask) {
3775 case TypeAttributes.Public:
3776 case TypeAttributes.NestedPublic:
3779 case TypeAttributes.NestedFamily:
3780 case TypeAttributes.NestedFamORAssem:
3788 protected override string GetConstructorDeclaration (MethodDefinition constructor)
3790 return GetMethodDeclaration (constructor);
3793 protected override string GetMethodDeclaration (MethodDefinition method)
3795 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
3798 var buf = new StringBuilder ();
3799 buf.Append (".method ");
3800 AppendVisibility (buf, method);
3801 if (method.IsStatic)
3802 buf.Append ("static ");
3803 if (method.IsHideBySig)
3804 buf.Append ("hidebysig ");
3805 if (method.IsPInvokeImpl) {
3806 var info = method.PInvokeInfo;
3807 buf.Append ("pinvokeimpl (\"")
3808 .Append (info.Module.Name)
3809 .Append ("\" as \"")
3810 .Append (info.EntryPoint)
3812 if (info.IsCharSetAuto)
3813 buf.Append (" auto");
3814 if (info.IsCharSetUnicode)
3815 buf.Append (" unicode");
3816 if (info.IsCharSetAnsi)
3817 buf.Append (" ansi");
3818 if (info.IsCallConvCdecl)
3819 buf.Append (" cdecl");
3820 if (info.IsCallConvStdCall)
3821 buf.Append (" stdcall");
3822 if (info.IsCallConvWinapi)
3823 buf.Append (" winapi");
3824 if (info.IsCallConvThiscall)
3825 buf.Append (" thiscall");
3826 if (info.SupportsLastError)
3827 buf.Append (" lasterr");
3830 if (method.IsSpecialName)
3831 buf.Append ("specialname ");
3832 if (method.IsRuntimeSpecialName)
3833 buf.Append ("rtspecialname ");
3834 if (method.IsNewSlot)
3835 buf.Append ("newslot ");
3836 if (method.IsVirtual)
3837 buf.Append ("virtual ");
3838 if (!method.IsStatic)
3839 buf.Append ("instance ");
3840 _AppendTypeName (buf, method.ReturnType);
3842 .Append (method.Name);
3843 if (method.IsGenericMethod ()) {
3844 var state = MemberFormatterState;
3845 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3846 IList<GenericParameter> args = method.GenericParameters;
3847 if (args.Count > 0) {
3849 _AppendTypeName (buf, args [0]);
3850 for (int i = 1; i < args.Count; ++i)
3851 _AppendTypeName (buf.Append (", "), args [i]);
3854 MemberFormatterState = state;
3859 for (int i = 0; i < method.Parameters.Count; ++i) {
3863 _AppendTypeName (buf, method.Parameters [i].ParameterType);
3865 buf.Append (method.Parameters [i].Name);
3869 buf.Append (" cil");
3870 if (method.IsRuntime)
3871 buf.Append (" runtime");
3872 if (method.IsManaged)
3873 buf.Append (" managed");
3875 return buf.ToString ();
3878 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3880 if (DocUtils.IsExplicitlyImplemented (method)) {
3881 TypeReference iface;
3882 MethodReference ifaceMethod;
3883 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3884 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3886 .Append (ifaceMethod.Name);
3888 return base.AppendMethodName (buf, method);
3891 protected override string RefTypeModifier {
3895 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3897 if (method.IsPublic)
3898 return buf.Append ("public ");
3899 if (method.IsFamilyAndAssembly)
3900 return buf.Append ("familyandassembly");
3901 if (method.IsFamilyOrAssembly)
3902 return buf.Append ("familyorassembly");
3903 if (method.IsFamily)
3904 return buf.Append ("family");
3908 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3910 string modifiers = String.Empty;
3911 if (method.IsStatic) modifiers += " static";
3912 if (method.IsVirtual && !method.IsAbstract) {
3913 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3914 else modifiers += " override";
3916 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
3917 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
3918 if (method.IsFinal) modifiers += " sealed";
3919 if (modifiers == " virtual sealed") modifiers = "";
3921 return buf.Append (modifiers);
3924 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3926 if (method.IsGenericMethod ()) {
3927 IList<GenericParameter> args = method.GenericParameters;
3928 if (args.Count > 0) {
3930 buf.Append (args [0].Name);
3931 for (int i = 1; i < args.Count; ++i)
3932 buf.Append (",").Append (args [i].Name);
3939 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
3941 return AppendParameters (buf, method, parameters, '(', ')');
3944 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
3948 if (parameters.Count > 0) {
3949 if (DocUtils.IsExtensionMethod (method))
3950 buf.Append ("this ");
3951 AppendParameter (buf, parameters [0]);
3952 for (int i = 1; i < parameters.Count; ++i) {
3954 AppendParameter (buf, parameters [i]);
3958 return buf.Append (end);
3961 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
3963 if (parameter.ParameterType is ByReferenceType) {
3964 if (parameter.IsOut)
3965 buf.Append ("out ");
3967 buf.Append ("ref ");
3969 buf.Append (GetName (parameter.ParameterType)).Append (" ");
3970 return buf.Append (parameter.Name);
3973 protected override string GetPropertyDeclaration (PropertyDefinition property)
3975 MethodDefinition gm = null, sm = null;
3977 string get_visible = null;
3978 if ((gm = property.GetMethod) != null &&
3979 (DocUtils.IsExplicitlyImplemented (gm) ||
3980 (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
3981 get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
3982 string set_visible = null;
3983 if ((sm = property.SetMethod) != null &&
3984 (DocUtils.IsExplicitlyImplemented (sm) ||
3985 (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
3986 set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
3988 if ((set_visible == null) && (get_visible == null))
3991 StringBuilder buf = new StringBuilder ()
3992 .Append (".property ");
3993 if (!(gm ?? sm).IsStatic)
3994 buf.Append ("instance ");
3995 _AppendTypeName (buf, property.PropertyType);
3996 buf.Append (' ').Append (property.Name);
3997 if (!property.HasParameters || property.Parameters.Count == 0)
3998 return buf.ToString ();
4002 foreach (ParameterDefinition p in property.Parameters) {
4006 _AppendTypeName (buf, p.ParameterType);
4010 return buf.ToString ();
4013 protected override string GetFieldDeclaration (FieldDefinition field)
4015 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4016 if (declType.IsEnum && field.Name == "value__")
4017 return null; // This member of enums aren't documented.
4019 StringBuilder buf = new StringBuilder ();
4020 AppendFieldVisibility (buf, field);
4021 if (buf.Length == 0)
4024 buf.Insert (0, ".field ");
4027 buf.Append ("static ");
4028 if (field.IsInitOnly)
4029 buf.Append ("initonly ");
4030 if (field.IsLiteral)
4031 buf.Append ("literal ");
4032 _AppendTypeName (buf, field.FieldType);
4033 buf.Append (' ').Append (field.Name);
4034 AppendFieldValue (buf, field);
4036 return buf.ToString ();
4039 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4042 return buf.Append ("public ");
4043 if (field.IsFamilyAndAssembly)
4044 return buf.Append ("familyandassembly ");
4045 if (field.IsFamilyOrAssembly)
4046 return buf.Append ("familyorassembly ");
4048 return buf.Append ("family ");
4052 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4054 // enums have a value__ field, which we ignore
4055 if (field.DeclaringType.IsGenericType ())
4057 if (field.HasConstant && field.IsLiteral) {
4060 val = field.Constant;
4065 buf.Append (" = ").Append ("null");
4066 else if (val is Enum)
4068 .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4070 .Append (val.ToString ())
4072 else if (val is IFormattable) {
4073 string value = ((IFormattable)val).ToString();
4076 buf.Append ("\"" + value + "\"");
4078 buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4087 protected override string GetEventDeclaration (EventDefinition e)
4089 StringBuilder buf = new StringBuilder ();
4090 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4095 buf.Append (".event ")
4096 .Append (GetName (e.EventType))
4100 return buf.ToString ();
4104 class ILMemberFormatter : ILFullMemberFormatter {
4105 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4111 class CSharpFullMemberFormatter : MemberFormatter {
4113 public override string Language {
4117 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4119 string ns = DocUtils.GetNamespace (type);
4120 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
4121 buf.Append (ns).Append ('.');
4125 private string GetCSharpType (string t)
4128 case "System.Byte": return "byte";
4129 case "System.SByte": return "sbyte";
4130 case "System.Int16": return "short";
4131 case "System.Int32": return "int";
4132 case "System.Int64": return "long";
4134 case "System.UInt16": return "ushort";
4135 case "System.UInt32": return "uint";
4136 case "System.UInt64": return "ulong";
4138 case "System.Single": return "float";
4139 case "System.Double": return "double";
4140 case "System.Decimal": return "decimal";
4141 case "System.Boolean": return "bool";
4142 case "System.Char": return "char";
4143 case "System.Void": return "void";
4144 case "System.String": return "string";
4145 case "System.Object": return "object";
4150 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
4152 if (type is GenericParameter)
4153 return AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
4154 string t = type.FullName;
4155 if (!t.StartsWith ("System.")) {
4156 return base.AppendTypeName (buf, type);
4159 string s = GetCSharpType (t);
4161 return buf.Append (s);
4163 return base.AppendTypeName (buf, type);
4166 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
4168 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
4170 GenericParameterAttributes attrs = type.Attributes;
4171 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
4172 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
4176 buf.Append ("out ");
4180 protected override string GetTypeDeclaration (TypeDefinition type)
4182 string visibility = GetTypeVisibility (type.Attributes);
4183 if (visibility == null)
4186 StringBuilder buf = new StringBuilder ();
4188 buf.Append (visibility);
4191 MemberFormatter full = new CSharpFullMemberFormatter ();
4193 if (DocUtils.IsDelegate (type)) {
4194 buf.Append("delegate ");
4195 MethodDefinition invoke = type.GetMethod ("Invoke");
4196 buf.Append (full.GetName (invoke.ReturnType)).Append (" ");
4197 buf.Append (GetName (type));
4198 AppendParameters (buf, invoke, invoke.Parameters);
4199 AppendGenericTypeConstraints (buf, type);
4202 return buf.ToString();
4205 if (type.IsAbstract && !type.IsInterface)
4206 buf.Append("abstract ");
4207 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
4208 buf.Append("sealed ");
4209 buf.Replace ("abstract sealed", "static");
4211 buf.Append (GetTypeKind (type));
4213 buf.Append (GetCSharpType (type.FullName) == null
4218 TypeReference basetype = type.BaseType;
4219 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
4222 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
4223 .Select (iface => full.GetName (iface))
4227 if (basetype != null || interface_names.Count > 0)
4230 if (basetype != null) {
4231 buf.Append (full.GetName (basetype));
4232 if (interface_names.Count > 0)
4236 for (int i = 0; i < interface_names.Count; i++){
4239 buf.Append (interface_names [i]);
4241 AppendGenericTypeConstraints (buf, type);
4244 return buf.ToString ();
4247 static string GetTypeKind (TypeDefinition t)
4253 if (t.IsClass || t.FullName == "System.Enum")
4257 throw new ArgumentException(t.FullName);
4260 static string GetTypeVisibility (TypeAttributes ta)
4262 switch (ta & TypeAttributes.VisibilityMask) {
4263 case TypeAttributes.Public:
4264 case TypeAttributes.NestedPublic:
4267 case TypeAttributes.NestedFamily:
4268 case TypeAttributes.NestedFamORAssem:
4276 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4278 if (type.GenericParameters.Count == 0)
4280 return AppendConstraints (buf, type.GenericParameters);
4283 private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
4285 foreach (GenericParameter genArg in genArgs) {
4286 GenericParameterAttributes attrs = genArg.Attributes;
4287 IList<TypeReference> constraints = genArg.Constraints;
4288 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
4291 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
4292 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
4293 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
4296 if (!isref && !isvt && !isnew && constraints.Count == 0)
4298 buf.Append (" where ").Append (genArg.Name).Append (" : ");
4300 buf.Append ("class");
4304 buf.Append ("struct");
4307 if (constraints.Count > 0 && !isvt) {
4310 buf.Append (GetTypeName (constraints [0]));
4311 for (int i = 1; i < constraints.Count; ++i)
4312 buf.Append (", ").Append (GetTypeName (constraints [i]));
4314 if (isnew && !isvt) {
4317 buf.Append ("new()");
4323 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4325 StringBuilder buf = new StringBuilder ();
4326 AppendVisibility (buf, constructor);
4327 if (buf.Length == 0)
4331 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
4332 AppendParameters (buf, constructor, constructor.Parameters);
4335 return buf.ToString ();
4338 protected override string GetMethodDeclaration (MethodDefinition method)
4340 string decl = base.GetMethodDeclaration (method);
4346 protected override string GetTypeName (MethodReturnType returnType)
4348 return GetTypeName (returnType, () => returnType.ReturnType);
4351 string GetTypeName (ICustomAttributeProvider provider, Func<TypeReference> selector)
4353 string type = GetName (selector ());
4354 if (type == "object" && provider.HasCustomAttributes &&
4355 provider.CustomAttributes.Cast<CustomAttribute>()
4356 .Any (ca => ca.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.DynamicAttribute"))
4361 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4363 if (DocUtils.IsExplicitlyImplemented (method)) {
4364 TypeReference iface;
4365 MethodReference ifaceMethod;
4366 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4367 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4369 .Append (ifaceMethod.Name);
4371 return base.AppendMethodName (buf, method);
4374 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4376 if (method.GenericParameters.Count == 0)
4378 return AppendConstraints (buf, method.GenericParameters);
4381 protected override string RefTypeModifier {
4385 protected override string GetFinalizerName (MethodDefinition method)
4387 return "~" + method.DeclaringType.Name + " ()";
4390 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4394 if (method.IsPublic)
4395 return buf.Append ("public");
4396 if (method.IsFamily || method.IsFamilyOrAssembly)
4397 return buf.Append ("protected");
4401 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4403 string modifiers = String.Empty;
4404 if (method.IsStatic) modifiers += " static";
4405 if (method.IsVirtual && !method.IsAbstract) {
4406 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4407 else modifiers += " override";
4409 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4410 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4411 if (method.IsFinal) modifiers += " sealed";
4412 if (modifiers == " virtual sealed") modifiers = "";
4414 return buf.Append (modifiers);
4417 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4419 if (method.IsGenericMethod ()) {
4420 IList<GenericParameter> args = method.GenericParameters;
4421 if (args.Count > 0) {
4423 buf.Append (args [0].Name);
4424 for (int i = 1; i < args.Count; ++i)
4425 buf.Append (",").Append (args [i].Name);
4432 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4434 return AppendParameters (buf, method, parameters, '(', ')');
4437 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4441 if (parameters.Count > 0) {
4442 if (DocUtils.IsExtensionMethod (method))
4443 buf.Append ("this ");
4444 AppendParameter (buf, parameters [0]);
4445 for (int i = 1; i < parameters.Count; ++i) {
4447 AppendParameter (buf, parameters [i]);
4451 return buf.Append (end);
4454 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4456 if (parameter.ParameterType is ByReferenceType) {
4457 if (parameter.IsOut)
4458 buf.Append ("out ");
4460 buf.Append ("ref ");
4462 buf.Append (GetTypeName (parameter, () => parameter.ParameterType)).Append (" ");
4463 return buf.Append (parameter.Name);
4466 protected override string GetPropertyDeclaration (PropertyDefinition property)
4468 MethodDefinition method;
4470 string get_visible = null;
4471 if ((method = property.GetMethod) != null &&
4472 (DocUtils.IsExplicitlyImplemented (method) ||
4473 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
4474 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
4475 string set_visible = null;
4476 if ((method = property.SetMethod) != null &&
4477 (DocUtils.IsExplicitlyImplemented (method) ||
4478 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
4479 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
4481 if ((set_visible == null) && (get_visible == null))
4485 StringBuilder buf = new StringBuilder ();
4486 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
4487 buf.Append (visibility = get_visible);
4488 else if (set_visible != null && get_visible == null)
4489 buf.Append (visibility = set_visible);
4491 buf.Append (visibility = "public");
4493 // Pick an accessor to use for static/virtual/override/etc. checks.
4494 method = property.SetMethod;
4496 method = property.GetMethod;
4498 string modifiers = String.Empty;
4499 if (method.IsStatic) modifiers += " static";
4500 if (method.IsVirtual && !method.IsAbstract) {
4501 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
4502 modifiers += " virtual";
4504 modifiers += " override";
4506 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
4507 if (method.IsAbstract && !declDef.IsInterface)
4508 modifiers += " abstract";
4510 modifiers += " sealed";
4511 if (modifiers == " virtual sealed")
4513 buf.Append (modifiers).Append (' ');
4515 buf.Append (GetName (property.PropertyType)).Append (' ');
4517 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
4518 string name = property.Name;
4519 foreach (MemberReference mi in defs) {
4520 if (mi == property) {
4525 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
4527 if (property.Parameters.Count != 0) {
4528 AppendParameters (buf, method, property.Parameters, '[', ']');
4532 if (get_visible != null) {
4533 if (get_visible != visibility)
4534 buf.Append (' ').Append (get_visible);
4535 buf.Append (" get;");
4537 if (set_visible != null) {
4538 if (set_visible != visibility)
4539 buf.Append (' ').Append (set_visible);
4540 buf.Append (" set;");
4544 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
4547 protected override string GetFieldDeclaration (FieldDefinition field)
4549 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4550 if (declType.IsEnum && field.Name == "value__")
4551 return null; // This member of enums aren't documented.
4553 StringBuilder buf = new StringBuilder ();
4554 AppendFieldVisibility (buf, field);
4555 if (buf.Length == 0)
4558 if (declType.IsEnum)
4561 if (field.IsStatic && !field.IsLiteral)
4562 buf.Append (" static");
4563 if (field.IsInitOnly)
4564 buf.Append (" readonly");
4565 if (field.IsLiteral)
4566 buf.Append (" const");
4568 buf.Append (' ').Append (GetName (field.FieldType)).Append (' ');
4569 buf.Append (field.Name);
4570 AppendFieldValue (buf, field);
4573 return buf.ToString ();
4576 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4579 return buf.Append ("public");
4580 if (field.IsFamily || field.IsFamilyOrAssembly)
4581 return buf.Append ("protected");
4585 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4587 // enums have a value__ field, which we ignore
4588 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
4589 field.DeclaringType.IsGenericType ())
4591 if (field.HasConstant && field.IsLiteral) {
4594 val = field.Constant;
4599 buf.Append (" = ").Append ("null");
4600 else if (val is Enum)
4601 buf.Append (" = ").Append (val.ToString ());
4602 else if (val is IFormattable) {
4603 string value = ((IFormattable)val).ToString();
4605 value = "\"" + value + "\"";
4606 buf.Append (" = ").Append (value);
4612 protected override string GetEventDeclaration (EventDefinition e)
4614 StringBuilder buf = new StringBuilder ();
4615 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4619 AppendModifiers (buf, e.AddMethod);
4621 buf.Append (" event ");
4622 buf.Append (GetName (e.EventType)).Append (' ');
4623 buf.Append (e.Name).Append (';');
4625 return buf.ToString ();
4629 class CSharpMemberFormatter : CSharpFullMemberFormatter {
4630 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4636 class DocTypeFullMemberFormatter : MemberFormatter {
4637 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
4639 protected override char NestedTypeSeparator {
4644 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
4645 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4651 class SlashDocMemberFormatter : MemberFormatter {
4653 protected override char[] GenericTypeContainer {
4654 get {return new char[]{'{', '}'};}
4657 private bool AddTypeCount = true;
4659 private TypeReference genDeclType;
4660 private MethodReference genDeclMethod;
4662 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
4664 if (type is GenericParameter) {
4666 if (genDeclType != null) {
4667 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
4668 for (int i = 0; i < genArgs.Count; ++i) {
4669 if (genArgs [i].Name == type.Name) {
4670 buf.Append ('`').Append (i);
4675 if (genDeclMethod != null) {
4676 IList<GenericParameter> genArgs = null;
4677 if (genDeclMethod.IsGenericMethod ()) {
4678 genArgs = genDeclMethod.GenericParameters;
4679 for (int i = 0; i < genArgs.Count; ++i) {
4680 if (genArgs [i].Name == type.Name) {
4681 buf.Append ("``").Append (i);
4687 if (genDeclType == null && genDeclMethod == null) {
4688 // Probably from within an explicitly implemented interface member,
4689 // where CSC uses parameter names instead of indices (why?), e.g.
4690 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
4691 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
4692 buf.Append (type.Name);
4694 if (buf.Length == l) {
4695 throw new Exception (string.Format (
4696 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
4697 type.Name, genDeclType, genDeclMethod));
4701 base.AppendTypeName (buf, type);
4703 int numArgs = type.GenericParameters.Count;
4704 if (type.DeclaringType != null)
4705 numArgs -= type.GenericParameters.Count;
4707 buf.Append ('`').Append (numArgs);
4714 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
4717 base.AppendGenericType (buf, type);
4719 AppendType (buf, type);
4723 private StringBuilder AppendType (StringBuilder buf, TypeReference type)
4725 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
4726 bool insertNested = false;
4727 int prevParamCount = 0;
4728 foreach (var decl in decls) {
4730 buf.Append (NestedTypeSeparator);
4731 insertNested = true;
4732 base.AppendTypeName (buf, decl);
4733 int argCount = DocUtils.GetGenericArgumentCount (decl);
4734 int numArgs = argCount - prevParamCount;
4735 prevParamCount = argCount;
4737 buf.Append ('`').Append (numArgs);
4742 public override string GetDeclaration (MemberReference member)
4744 TypeReference r = member as TypeReference;
4746 return "T:" + GetTypeName (r);
4748 return base.GetDeclaration (member);
4751 protected override string GetConstructorName (MethodReference constructor)
4753 return GetMethodDefinitionName (constructor, "#ctor");
4756 protected override string GetMethodName (MethodReference method)
4759 MethodDefinition methodDef = method as MethodDefinition;
4760 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
4763 TypeReference iface;
4764 MethodReference ifaceMethod;
4765 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
4766 AddTypeCount = false;
4767 name = GetTypeName (iface) + "." + ifaceMethod.Name;
4768 AddTypeCount = true;
4770 return GetMethodDefinitionName (method, name);
4773 private string GetMethodDefinitionName (MethodReference method, string name)
4775 StringBuilder buf = new StringBuilder ();
4776 buf.Append (GetTypeName (method.DeclaringType));
4778 buf.Append (name.Replace (".", "#"));
4779 if (method.IsGenericMethod ()) {
4780 IList<GenericParameter> genArgs = method.GenericParameters;
4781 if (genArgs.Count > 0)
4782 buf.Append ("``").Append (genArgs.Count);
4784 IList<ParameterDefinition> parameters = method.Parameters;
4786 genDeclType = method.DeclaringType;
4787 genDeclMethod = method;
4788 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
4792 genDeclMethod = null;
4794 return buf.ToString ();
4797 private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
4799 if (parameters.Count == 0)
4804 AppendParameter (buf, genArgs, parameters [0]);
4805 for (int i = 1; i < parameters.Count; ++i) {
4807 AppendParameter (buf, genArgs, parameters [i]);
4810 return buf.Append (')');
4813 private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
4815 AddTypeCount = false;
4816 buf.Append (GetTypeName (parameter.ParameterType));
4817 AddTypeCount = true;
4821 protected override string GetPropertyName (PropertyReference property)
4825 PropertyDefinition propertyDef = property as PropertyDefinition;
4826 MethodDefinition method = null;
4827 if (propertyDef != null)
4828 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
4829 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
4830 name = property.Name;
4832 TypeReference iface;
4833 MethodReference ifaceMethod;
4834 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4835 AddTypeCount = false;
4836 name = string.Join ("#", new string[]{
4837 GetTypeName (iface).Replace (".", "#"),
4838 DocUtils.GetMember (property.Name)
4840 AddTypeCount = true;
4843 StringBuilder buf = new StringBuilder ();
4844 buf.Append (GetName (property.DeclaringType));
4847 IList<ParameterDefinition> parameters = property.Parameters;
4848 if (parameters.Count > 0) {
4849 genDeclType = property.DeclaringType;
4851 IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
4852 AppendParameter (buf, genArgs, parameters [0]);
4853 for (int i = 1; i < parameters.Count; ++i) {
4855 AppendParameter (buf, genArgs, parameters [i]);
4860 return buf.ToString ();
4863 protected override string GetFieldName (FieldReference field)
4865 return string.Format ("{0}.{1}",
4866 GetName (field.DeclaringType), field.Name);
4869 protected override string GetEventName (EventReference e)
4871 return string.Format ("{0}.{1}",
4872 GetName (e.DeclaringType), e.Name);
4875 protected override string GetTypeDeclaration (TypeDefinition type)
4877 string name = GetName (type);
4883 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4885 string name = GetName (constructor);
4891 protected override string GetMethodDeclaration (MethodDefinition method)
4893 string name = GetName (method);
4896 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4897 genDeclType = method.DeclaringType;
4898 genDeclMethod = method;
4899 name += "~" + GetName (method.ReturnType);
4901 genDeclMethod = null;
4906 protected override string GetPropertyDeclaration (PropertyDefinition property)
4908 string name = GetName (property);
4914 protected override string GetFieldDeclaration (FieldDefinition field)
4916 string name = GetName (field);
4922 protected override string GetEventDeclaration (EventDefinition e)
4924 string name = GetName (e);
4931 class FileNameMemberFormatter : SlashDocMemberFormatter {
4932 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4937 protected override char NestedTypeSeparator {