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;
33 bool no_assembly_versions;
34 ExceptionLocations? exceptions;
36 int additions = 0, deletions = 0;
38 static XmlDocument slashdocs;
43 static readonly MemberFormatter csharpFullFormatter = new CSharpFullMemberFormatter ();
44 static readonly MemberFormatter csharpFormatter = new CSharpMemberFormatter ();
45 static readonly MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
46 static readonly MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
47 static readonly MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
49 MyXmlNodeList extensionMethods = new MyXmlNodeList ();
51 public override void Run (IEnumerable<string> args)
53 show_exceptions = DebugOutput;
55 var types = new List<string> ();
56 var p = new OptionSet () {
58 "Delete removed members from the XML files.",
59 v => delete = v != null },
61 "Document potential exceptions that members can generate. {SOURCES} " +
62 "is a comma-separated list of:\n" +
63 " asm Method calls in same assembly\n" +
64 " depasm Method calls in dependent assemblies\n" +
65 " all Record all possible exceptions\n" +
66 "If nothing is specified, then only exceptions from the member will " +
68 v => exceptions = ParseExceptionLocations (v) },
70 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
73 case "no-assembly-versions":
74 no_assembly_versions = true;
77 throw new Exception ("Unsupported flag `" + v + "'.");
80 { "fno-assembly-versions",
81 "Do not generate //AssemblyVersion elements.",
82 v => no_assembly_versions = v != null },
84 "Import documentation from {FILE}.",
87 "Root {DIRECTORY} to generate/update documentation.",
90 "Manually specify the assembly {VERSION} that new members were added in.",
93 "Only update documentation for {TYPE}.",
96 var assemblies = Parse (p, args, "update",
97 "[OPTIONS]+ ASSEMBLIES",
98 "Create or update documentation from ASSEMBLIES.");
99 if (assemblies == null)
101 if (assemblies.Count == 0)
102 Error ("No assemblies specified.");
104 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
107 throw new InvalidOperationException("The --out option is required.");
109 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
111 if (import != null && ecmadocs == null && slashdocs == null) {
113 XmlReader r = new XmlTextReader (import);
115 while (r.NodeType != XmlNodeType.Element) {
117 Error ("Unable to read XML file: {0}.", import);
119 if (r.LocalName == "doc") {
120 slashdocs = new XmlDocument();
121 slashdocs.Load (import);
123 else if (r.LocalName == "Libraries") {
124 ecmadocs = new XmlTextReader (import);
127 Error ("Unsupported XML format within {0}.", import);
130 } catch (Exception e) {
131 Environment.ExitCode = 1;
132 Error ("Could not load XML file: {0}.", e.Message);
136 // PERFORM THE UPDATES
139 DoUpdateTypes (srcPath, types, srcPath);
141 else if (opts.@namespace != null)
142 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
143 Path.Combine (dest_dir, opts.@namespace));
146 DoUpdateAssemblies (srcPath, srcPath);
148 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
151 static ExceptionLocations ParseExceptionLocations (string s)
153 ExceptionLocations loc = ExceptionLocations.Member;
156 foreach (var type in s.Split (',')) {
158 case "asm": loc |= ExceptionLocations.Assembly; break;
159 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
160 case "all": loc = ExceptionLocations.All; break;
161 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
167 private void Warning (string format, params object[] args)
169 Message (TraceLevel.Warning, "mdoc: " + format, args);
172 private static AssemblyDefinition LoadAssembly (string name)
174 AssemblyDefinition assembly = null;
176 assembly = AssemblyFactory.GetAssembly (name);
177 } catch (System.IO.FileNotFoundException) { }
179 if (assembly == null)
180 throw new InvalidOperationException("Assembly " + name + " not found.");
182 var r = assembly.Resolver as BaseAssemblyResolver;
183 if (r != null && name.Contains (Path.DirectorySeparatorChar)) {
184 r.AddSearchDirectory (Path.GetDirectoryName (name));
189 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
190 OrderTypeAttributes (element);
191 XmlTextWriter writer = new XmlTextWriter(output);
192 writer.Formatting = Formatting.Indented;
193 writer.Indentation = 2;
194 writer.IndentChar = ' ';
195 element.WriteTo(writer);
199 private static void OrderTypeAttributes (XmlElement e)
201 foreach (XmlElement type in e.SelectNodes ("//Type")) {
202 OrderTypeAttributes (type.Attributes);
206 static readonly string[] TypeAttributeOrder = {
207 "Name", "FullName", "FullNameSP", "Maintainer"
210 private static void OrderTypeAttributes (XmlAttributeCollection c)
212 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
213 for (int i = 0; i < c.Count; ++i) {
214 XmlAttribute a = c [i];
215 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
216 if (a.Name == TypeAttributeOrder [j]) {
222 for (int i = attrs.Length-1; i >= 0; --i) {
223 XmlAttribute n = attrs [i];
226 XmlAttribute r = null;
227 for (int j = i+1; j < attrs.Length; ++j) {
228 if (attrs [j] != null) {
236 c.InsertBefore (n, r);
240 private XmlDocument CreateIndexStub()
242 XmlDocument index = new XmlDocument();
244 XmlElement index_root = index.CreateElement("Overview");
245 index.AppendChild(index_root);
247 if (assemblies.Count == 0)
248 throw new Exception ("No assembly");
250 XmlElement index_assemblies = index.CreateElement("Assemblies");
251 index_root.AppendChild(index_assemblies);
253 XmlElement index_remarks = index.CreateElement("Remarks");
254 index_remarks.InnerText = "To be added.";
255 index_root.AppendChild(index_remarks);
257 XmlElement index_copyright = index.CreateElement("Copyright");
258 index_copyright.InnerText = "To be added.";
259 index_root.AppendChild(index_copyright);
261 XmlElement index_types = index.CreateElement("Types");
262 index_root.AppendChild(index_types);
267 private static void WriteNamespaceStub(string ns, string outdir) {
268 XmlDocument index = new XmlDocument();
270 XmlElement index_root = index.CreateElement("Namespace");
271 index.AppendChild(index_root);
273 index_root.SetAttribute("Name", ns);
275 XmlElement index_docs = index.CreateElement("Docs");
276 index_root.AppendChild(index_docs);
278 XmlElement index_summary = index.CreateElement("summary");
279 index_summary.InnerText = "To be added.";
280 index_docs.AppendChild(index_summary);
282 XmlElement index_remarks = index.CreateElement("remarks");
283 index_remarks.InnerText = "To be added.";
284 index_docs.AppendChild(index_remarks);
286 using (TextWriter writer = OpenWrite (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew)) {
287 WriteXml(index.DocumentElement, writer);
291 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
293 var found = new HashSet<string> ();
294 foreach (AssemblyDefinition assembly in assemblies) {
295 foreach (DocsTypeInfo docsTypeInfo in GetTypes (assembly, typenames)) {
296 string relpath = DoUpdateType (docsTypeInfo.Type, basepath, dest, docsTypeInfo.EcmaDocs);
298 found.Add (docsTypeInfo.Type.FullName);
301 var notFound = from n in typenames where !found.Contains (n) select n;
303 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
306 public string DoUpdateType (TypeDefinition type, string basepath, string dest, XmlReader ecmaDocsType)
308 if (type.Namespace == null)
309 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
311 if (!IsPublic (type))
314 // Must get the A+B form of the type name.
315 string typename = GetTypeFileName(type);
317 string reltypefile = DocUtils.PathCombine (DocUtils.GetNamespace (type), typename + ".xml");
318 string typefile = Path.Combine (basepath, reltypefile);
319 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
321 string output = null;
324 } else if (dest == "-") {
327 output = Path.Combine (dest, reltypefile);
332 XmlDocument basefile = new XmlDocument();
334 basefile.Load(typefile);
335 } catch (Exception e) {
336 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
339 DoUpdateType2("Updating", basefile, type, output, false, ecmaDocsType);
342 XmlElement td = StubType(type, output, ecmaDocsType);
346 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
349 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
355 public void DoUpdateNS (string ns, string nspath, string outpath)
357 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
358 AssemblyDefinition assembly = assemblies [0];
360 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
361 XmlDocument basefile = new XmlDocument();
362 string typefile = Path.Combine(nspath, file.Name);
364 basefile.Load(typefile);
365 } catch (Exception e) {
366 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
370 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
371 TypeDefinition type = assembly.GetType(typename);
373 Warning ("Type no longer in assembly: " + typename);
377 seenTypes[type] = seenTypes;
378 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false, null);
381 // Stub types not in the directory
382 foreach (DocsTypeInfo docsTypeInfo in GetTypes (assembly, null)) {
383 TypeDefinition type = docsTypeInfo.Type;
384 if (type.Namespace != ns || seenTypes.ContainsKey(type))
387 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"), docsTypeInfo.EcmaDocs);
388 if (td == null) continue;
392 private static string GetTypeFileName (TypeReference type)
394 return filenameFormatter.GetName (type);
397 public static string GetTypeFileName (string typename)
399 StringBuilder filename = new StringBuilder (typename.Length);
403 for (int i = 0; i < typename.Length; ++i) {
404 char c = typename [i];
413 filename.Append ('`').Append ((numArgs+1).ToString());
428 return filename.ToString ();
431 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
433 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
434 index_assembly.SetAttribute ("Name", assembly.Name.Name);
435 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
436 MakeAttributes (index_assembly, assembly.CustomAttributes, true);
437 parent.AppendChild(index_assembly);
440 private void DoUpdateAssemblies (string source, string dest)
442 string indexfile = dest + "/index.xml";
444 if (System.IO.File.Exists(indexfile)) {
445 index = new XmlDocument();
446 index.Load(indexfile);
449 ClearElement(index.DocumentElement, "Assembly");
450 ClearElement(index.DocumentElement, "Attributes");
452 index = CreateIndexStub();
455 string defaultTitle = "Untitled";
456 if (assemblies.Count == 1)
457 defaultTitle = assemblies[0].Name.Name;
458 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
460 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
461 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
462 index_assemblies.RemoveAll ();
465 HashSet<string> goodfiles = new HashSet<string> ();
467 foreach (AssemblyDefinition assm in assemblies) {
468 AddIndexAssembly (assm, index_assemblies);
469 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
472 SortIndexEntries (index_types);
474 CleanupFiles (dest, goodfiles);
475 CleanupIndexTypes (index_types, goodfiles);
476 CleanupExtensions (index_types);
478 using (TextWriter writer = OpenWrite (indexfile, FileMode.Create))
479 WriteXml(index.DocumentElement, writer);
482 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
484 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
486 foreach (DocsTypeInfo docTypeInfo in GetTypes (assembly, null)) {
487 TypeDefinition type = docTypeInfo.Type;
488 string typename = GetTypeFileName(type);
489 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0)
492 string reltypepath = DoUpdateType (type, source, dest, docTypeInfo.EcmaDocs);
493 if (reltypepath == null)
496 // Add namespace and type nodes into the index file as needed
497 string ns = DocUtils.GetNamespace (type);
498 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode("Namespace[@Name='" + ns + "']");
499 if (nsnode == null) {
500 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
501 nsnode.SetAttribute ("Name", ns);
502 index_types.AppendChild(nsnode);
504 string doc_typename = GetDocTypeName (type);
505 XmlElement typenode = (XmlElement)nsnode.SelectSingleNode("Type[@Name='" + typename + "']");
506 if (typenode == null) {
507 typenode = index_types.OwnerDocument.CreateElement("Type");
508 typenode.SetAttribute("Name", typename);
509 nsnode.AppendChild(typenode);
511 if (typename != doc_typename)
512 typenode.SetAttribute("DisplayName", doc_typename);
514 typenode.RemoveAttribute("DisplayName");
515 typenode.SetAttribute ("Kind", GetTypeKind (type));
517 // Ensure the namespace index file exists
518 string onsdoc = DocUtils.PathCombine (dest, type.Namespace + ".xml");
519 string nsdoc = DocUtils.PathCombine (dest, "ns-" + type.Namespace + ".xml");
520 if (File.Exists (onsdoc)) {
521 File.Move (onsdoc, nsdoc);
524 if (!File.Exists (nsdoc)) {
525 Console.WriteLine("New Namespace File: " + type.Namespace);
526 WriteNamespaceStub(type.Namespace, dest);
529 goodfiles.Add (reltypepath);
534 public TypeDefinition Type;
535 public XmlReader EcmaDocs;
537 public DocsTypeInfo (TypeDefinition type, XmlReader docs)
540 this.EcmaDocs = docs;
544 IEnumerable<Mono.Documentation.MDocUpdater.DocsTypeInfo> GetTypes (AssemblyDefinition assembly, List<string> forTypes)
546 HashSet<string> seen = null;
547 if (forTypes != null)
549 if (ecmadocs != null) {
550 seen = new HashSet<string> ();
552 while (ecmadocs.Read ()) {
553 switch (ecmadocs.Name) {
556 typeDepth = ecmadocs.Depth;
557 if (ecmadocs.NodeType != XmlNodeType.Element)
559 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
561 string typename = ecmadocs.GetAttribute ("FullName");
562 string typename2 = GetTypeFileName (typename);
563 if (forTypes != null &&
564 forTypes.BinarySearch (typename) < 0 &&
565 typename != typename2 &&
566 forTypes.BinarySearch (typename2) < 0)
569 if ((t = assembly.GetType (typename)) == null &&
570 (t = assembly.GetType (typename2)) == null)
573 if (typename != typename2)
574 seen.Add (typename2);
575 Console.WriteLine (" Import: {0}", t.FullName);
576 yield return new DocsTypeInfo (t, ecmadocs);
584 foreach (TypeDefinition type in assembly.GetTypes()) {
585 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
587 if (seen != null && seen.Contains (type.FullName))
589 yield return new DocsTypeInfo (type, null);
590 foreach (TypeDefinition nested in type.NestedTypes)
591 yield return new DocsTypeInfo (nested, null);
595 private static void SortIndexEntries (XmlElement indexTypes)
597 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
598 XmlNodeComparer c = new AttributeNameComparer ();
599 SortXmlNodes (indexTypes, namespaces, c);
601 for (int i = 0; i < namespaces.Count; ++i)
602 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
605 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
607 MyXmlNodeList l = new MyXmlNodeList (children.Count);
608 for (int i = 0; i < children.Count; ++i)
609 l.Add (children [i]);
611 for (int i = l.Count - 1; i > 0; --i) {
612 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
616 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
618 public abstract int Compare (XmlNode x, XmlNode y);
620 public int Compare (object x, object y)
622 return Compare ((XmlNode) x, (XmlNode) y);
626 class AttributeNameComparer : XmlNodeComparer {
629 public AttributeNameComparer ()
634 public AttributeNameComparer (string attribute)
636 this.attribute = attribute;
639 public override int Compare (XmlNode x, XmlNode y)
641 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
645 class VersionComparer : XmlNodeComparer {
646 public override int Compare (XmlNode x, XmlNode y)
648 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
649 string a = GetVersion (x.InnerText);
650 string b = GetVersion (y.InnerText);
651 return new Version (a).CompareTo (new Version (b));
654 static string GetVersion (string v)
656 int n = v.IndexOf ("x");
659 return v.Substring (0, n-1);
663 private static string GetTypeKind (TypeDefinition type)
666 return "Enumeration";
667 if (type.IsValueType)
669 if (type.IsInterface)
671 if (DocUtils.IsDelegate (type))
673 if (type.IsClass || type.FullName == "System.Enum") // FIXME
675 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
678 private static bool IsPublic (TypeDefinition type)
680 TypeDefinition decl = type;
681 while (decl != null) {
682 if (!(decl.IsPublic || decl.IsNestedPublic)) {
685 decl = (TypeDefinition) decl.DeclaringType;
690 private void CleanupFiles (string dest, HashSet<string> goodfiles)
692 // Look for files that no longer correspond to types
693 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
694 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
695 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
696 if (!goodfiles.Contains (relTypeFile)) {
697 XmlDocument doc = new XmlDocument ();
698 doc.Load (typefile.FullName);
699 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
700 if (UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
701 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
702 WriteXml(doc.DocumentElement, writer);
703 goodfiles.Add (relTypeFile);
706 string newname = typefile.FullName + ".remove";
707 try { System.IO.File.Delete(newname); } catch (Exception) { }
708 try { typefile.MoveTo(newname); } catch (Exception) { }
709 Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
715 private static TextWriter OpenWrite (string path, FileMode mode)
717 return new StreamWriter (
718 new FileStream (path, mode),
719 new UTF8Encoding (false)
723 private string[] GetAssemblyVersions ()
725 return (from a in assemblies select GetAssemblyVersion (a)).ToArray ();
728 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
730 // Look for type nodes that no longer correspond to types
731 MyXmlNodeList remove = new MyXmlNodeList ();
732 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
733 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
734 if (!goodfiles.Contains (fulltypename)) {
735 remove.Add (typenode);
738 foreach (XmlNode n in remove)
739 n.ParentNode.RemoveChild (n);
742 private void CleanupExtensions (XmlElement index_types)
744 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
745 if (extensionMethods.Count == 0) {
748 index_types.RemoveChild (e);
752 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
753 index_types.SelectSingleNode ("/Overview").AppendChild (e);
757 extensionMethods.Sort (DefaultExtensionMethodComparer);
758 foreach (XmlNode m in extensionMethods) {
759 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
763 class ExtensionMethodComparer : XmlNodeComparer {
764 public override int Compare (XmlNode x, XmlNode y)
766 XmlNode xLink = x.SelectSingleNode ("Member/Link");
767 XmlNode yLink = y.SelectSingleNode ("Member/Link");
769 int n = xLink.Attributes ["Type"].Value.CompareTo (
770 yLink.Attributes ["Type"].Value);
773 n = xLink.Attributes ["Member"].Value.CompareTo (
774 yLink.Attributes ["Member"].Value);
775 if (n == 0 && !object.ReferenceEquals (x, y))
776 throw new InvalidOperationException ("Duplicate extension method found!");
781 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
783 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince, XmlReader ecmaDocsType)
785 Console.WriteLine(message + ": " + type.FullName);
787 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
789 // Update type metadata
790 UpdateType(basefile.DocumentElement, type, ecmaDocsType);
792 if (ecmaDocsType != null) {
793 while (ecmaDocsType.Name != "Members" && ecmaDocsType.Read ()) {
796 if (ecmaDocsType.IsEmptyElement)
800 // Update existing members. Delete member nodes that no longer should be there,
801 // and remember what members are already documented so we don't add them again.
803 MyXmlNodeList todelete = new MyXmlNodeList ();
804 foreach (DocsNodeInfo info in GetDocumentationMembers (basefile, type, ecmaDocsType)) {
805 XmlElement oldmember = info.Node;
806 IMemberReference oldmember2 = info.Member;
807 string sig = oldmember2 != null ? MakeMemberSignature(oldmember2) : null;
809 // Interface implementations and overrides are deleted from the docs
810 // unless the overrides option is given.
811 if (oldmember2 != null && sig == null)
814 // Deleted (or signature changed)
815 if (oldmember2 == null) {
816 if (UpdateAssemblyVersions (oldmember, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
818 DeleteMember ("Member Removed", output, oldmember, todelete);
823 if (seenmembers.ContainsKey (sig)) {
824 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
825 // ignore, already seen
827 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
828 DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
830 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
834 // Update signature information
837 seenmembers.Add (sig, oldmember);
839 foreach (XmlElement oldmember in todelete)
840 oldmember.ParentNode.RemoveChild (oldmember);
843 if (!DocUtils.IsDelegate (type)) {
844 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
845 foreach (IMemberReference m in type.GetMembers()) {
846 if (m is TypeDefinition) continue;
848 string sig = MakeMemberSignature(m);
849 if (sig == null) continue;
850 if (seenmembers.ContainsKey(sig)) continue;
852 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
853 if (mm == null) continue;
854 members.AppendChild( mm );
856 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
861 // Import code snippets from files
862 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
863 if (!(code is XmlElement)) continue;
864 string file = ((XmlElement)code).GetAttribute("src");
865 string lang = ((XmlElement)code).GetAttribute("lang");
867 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
869 code.InnerText = src;
873 if (insertSince && since != null) {
874 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
875 docs.AppendChild (CreateSinceNode (basefile));
879 XmlElement d = basefile.DocumentElement ["Docs"];
880 XmlElement m = basefile.DocumentElement ["Members"];
881 if (d != null && m != null)
882 basefile.DocumentElement.InsertBefore (
883 basefile.DocumentElement.RemoveChild (d), m);
887 System.IO.TextWriter writer;
889 writer = Console.Out;
891 FileInfo file = new FileInfo (output);
892 if (!file.Directory.Exists) {
893 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
894 file.Directory.Create ();
896 writer = OpenWrite (output, FileMode.Create);
900 WriteXml(basefile.DocumentElement, writer);
903 private string GetCodeSource (string lang, string file)
906 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
907 // Grab the specified region
908 string region = "#region " + file.Substring (anchorStart + 4);
909 file = file.Substring (0, anchorStart + 3);
911 using (StreamReader reader = new StreamReader (file)) {
913 StringBuilder src = new StringBuilder ();
915 while ((line = reader.ReadLine ()) != null) {
916 if (line.Trim() == region) {
917 indent = line.IndexOf (region);
920 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
925 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
928 return src.ToString ();
930 } catch (Exception e) {
931 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
932 file, region, show_exceptions ? e.ToString () : e.Message);
937 using (StreamReader reader = new StreamReader (file))
938 return reader.ReadToEnd ();
939 } catch (Exception e) {
940 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
945 private IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type, XmlReader ecmaDocsMembers)
947 if (ecmaDocsMembers != null) {
948 int membersDepth = ecmaDocsMembers.Depth;
950 while (go && ecmaDocsMembers.Read ()) {
951 switch (ecmaDocsMembers.Name) {
953 if (membersDepth != ecmaDocsMembers.Depth - 1 || ecmaDocsMembers.NodeType != XmlNodeType.Element)
955 DocumentationMember dm = new DocumentationMember (ecmaDocsMembers);
956 string xp = GetXPathForMember (dm);
957 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
959 if (oldmember == null) {
960 m = GetMember (type, dm);
962 Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
963 type.FullName, dm.MemberSignatures ["C#"]);
964 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
967 // oldmember lookup may have failed due to type parameter renames.
969 oldmember = (XmlElement) basefile.SelectSingleNode (GetXPathForMember (m));
970 if (oldmember == null) {
971 XmlElement members = WriteElement(basefile.DocumentElement, "Members");
972 oldmember = basefile.CreateElement ("Member");
973 oldmember.SetAttribute ("MemberName", dm.MemberName);
974 members.AppendChild (oldmember);
975 foreach (string key in Sort (dm.MemberSignatures.Keys)) {
976 XmlElement ms = basefile.CreateElement ("MemberSignature");
977 ms.SetAttribute ("Language", key);
978 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
979 oldmember.AppendChild (ms);
981 oldmember.SetAttribute ("__monodocer-seen__", "true");
982 Console.WriteLine ("Member Added: {0}", MakeMemberSignature (m));
987 m = GetMember (type, new DocumentationMember (oldmember));
989 Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
990 type.FullName, dm.MemberSignatures ["C#"]);
993 oldmember.SetAttribute ("__monodocer-seen__", "true");
995 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
996 if (ecmaDocsMembers.Name != "Docs")
997 throw new InvalidOperationException ("Found " + ecmaDocsMembers.Name + "; expected <Docs/>!");
998 node.EcmaDocs = ecmaDocsMembers;
1003 if (membersDepth == ecmaDocsMembers.Depth && ecmaDocsMembers.NodeType == XmlNodeType.EndElement) {
1010 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
1011 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
1012 oldmember.RemoveAttribute ("__monodocer-seen__");
1015 IMemberReference m = GetMember (type, new DocumentationMember (oldmember));
1017 yield return new DocsNodeInfo (oldmember);
1020 yield return new DocsNodeInfo (oldmember, m);
1025 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
1027 string format = output != null
1028 ? "{0}: File='{1}'; Signature='{4}'"
1029 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1033 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1034 member.Attributes ["MemberName"].Value,
1035 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
1036 if (!delete && MemberDocsHaveUserContent (member)) {
1037 Warning ("Member deletions must be enabled with the --delete option.");
1039 todelete.Add (member);
1044 class MemberComparer : XmlNodeComparer {
1045 public override int Compare (XmlNode x, XmlNode y)
1048 string xMemberName = x.Attributes ["MemberName"].Value;
1049 string yMemberName = y.Attributes ["MemberName"].Value;
1051 // generic methods *end* with '>'
1052 // it's possible for explicitly implemented generic interfaces to
1053 // contain <...> without being a generic method
1054 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1055 (r = xMemberName.CompareTo (yMemberName)) != 0)
1059 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1060 xMemberName = xMemberName.Substring (0, lt);
1061 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1062 yMemberName = yMemberName.Substring (0, lt);
1063 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1066 // if @MemberName matches, then it's either two different types of
1067 // members sharing the same name, e.g. field & property, or it's an
1068 // overloaded method.
1069 // for different type, sort based on MemberType value.
1070 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1071 y.SelectSingleNode ("MemberType").InnerText);
1075 // same type -- must be an overloaded method. Sort based on type
1076 // parameter count, then parameter count, then by the parameter
1078 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1079 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1080 if (xTypeParams.Count != yTypeParams.Count)
1081 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1082 for (int i = 0; i < xTypeParams.Count; ++i) {
1083 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1084 yTypeParams [i].Attributes ["Name"].Value);
1089 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1090 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1091 if (xParams.Count != yParams.Count)
1092 return xParams.Count <= yParams.Count ? -1 : 1;
1093 for (int i = 0; i < xParams.Count; ++i) {
1094 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1095 yParams [i].Attributes ["Type"].Value);
1099 // all parameters match, but return value might not match if it was
1100 // changed between one version and another.
1101 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1102 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1103 if (xReturn != null && yReturn != null) {
1104 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1113 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1115 private static void SortTypeMembers (XmlNode members)
1117 if (members == null)
1119 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1122 private static bool MemberDocsHaveUserContent (XmlNode e)
1124 e = (XmlElement)e.SelectSingleNode("Docs");
1125 if (e == null) return false;
1126 foreach (XmlElement d in e.SelectNodes("*"))
1127 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1132 // UPDATE HELPER FUNCTIONS
1134 private static IMemberReference GetMember (TypeDefinition type, DocumentationMember member)
1136 string membertype = member.MemberType;
1138 string returntype = member.ReturnType;
1140 string docName = member.MemberName;
1141 string[] docTypeParams = GetTypeParameters (docName);
1143 // Loop through all members in this type with the same name
1144 foreach (IMemberReference mi in GetReflectionMembers (type, docName)) {
1145 if (mi is TypeDefinition) continue;
1146 if (GetMemberType(mi) != membertype) continue;
1148 string sig = MakeMemberSignature(mi);
1149 if (sig == null) continue; // not publicly visible
1151 ParameterDefinitionCollection pis = null;
1152 string[] typeParams = null;
1153 if (mi is MethodDefinition) {
1154 MethodDefinition mb = (MethodDefinition) mi;
1155 pis = mb.Parameters;
1156 if (docTypeParams != null && mb.IsGenericMethod ()) {
1157 GenericParameterCollection args = mb.GenericParameters;
1158 if (args.Count == docTypeParams.Length) {
1159 typeParams = args.Cast<GenericParameter> ().Select (p => p.Name).ToArray ();
1163 else if (mi is PropertyDefinition)
1164 pis = ((PropertyDefinition)mi).Parameters;
1166 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
1167 int pcount = pis == null ? 0 : pis.Count;
1168 if (mcount != pcount)
1171 MethodDefinition mDef = mi as MethodDefinition;
1172 if (mDef != null && !mDef.IsConstructor) {
1173 // Casting operators can overload based on return type.
1174 if (returntype != GetReplacedString (
1175 GetDocTypeFullName (((MethodDefinition)mi).ReturnType.ReturnType),
1176 typeParams, docTypeParams)) {
1184 for (int i = 0; i < pis.Count; i++) {
1185 string paramType = GetReplacedString (
1186 GetDocParameterType (pis [i].ParameterType),
1187 typeParams, docTypeParams);
1188 if (paramType != (string) member.Parameters [i]) {
1193 if (!good) continue;
1201 private static IEnumerable<IMemberReference> GetReflectionMembers (TypeDefinition type, string docName)
1203 // need to worry about 4 forms of //@MemberName values:
1204 // 1. "Normal" (non-generic) member names: GetEnumerator
1206 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
1207 // - try as-is, and try type.member (due to "kludge" for property
1209 // 3. "Normal" Generic member names: Sort<T> (CSC)
1210 // - need to remove generic parameters --> "Sort"
1211 // 4. Explicitly-implemented interface members for generic interfaces:
1212 // -- System.Collections.Generic.IEnumerable<T>.Current
1213 // - Try as-is, and try type.member, *keeping* the generic parameters.
1214 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
1215 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
1216 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
1217 // this as (1) or (2).
1218 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
1220 foreach (IMemberReference mi in type.GetMembers (docName))
1222 if (CountChars (docName, '.') > 0)
1223 // might be a property; try only type.member instead of
1224 // namespace.type.member.
1225 foreach (IMemberReference mi in
1226 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
1233 int startLt, startType, startMethod;
1234 startLt = startType = startMethod = -1;
1235 for (int i = 0; i < docName.Length; ++i) {
1236 switch (docName [i]) {
1245 if (numLt == 0 && (i + 1) < docName.Length)
1246 // there's another character in docName, so this <...> sequence is
1247 // probably part of a generic type -- case 4.
1251 startType = startMethod;
1257 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
1259 foreach (IMemberReference mi in type.GetMembers (refName))
1263 foreach (IMemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
1266 // If we _still_ haven't found it, we've hit another generic naming issue:
1267 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
1268 // explicitly-implemented METHOD names (not properties), e.g.
1269 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
1270 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
1271 // which the XML docs will contain.
1273 // Alas, we can't derive the Mono name from docName, so we need to iterate
1274 // over all member names, convert them into CSC format, and compare... :-(
1277 foreach (IMemberReference mi in type.GetMembers ()) {
1278 if (GetMemberName (mi) == docName)
1283 static string[] GetTypeParameters (string docName)
1285 if (docName [docName.Length-1] != '>')
1287 StringList types = new StringList ();
1288 int endToken = docName.Length-2;
1289 int i = docName.Length-2;
1291 if (docName [i] == ',' || docName [i] == '<') {
1292 types.Add (docName.Substring (i + 1, endToken - i));
1295 if (docName [i] == '<')
1300 return types.ToArray ();
1303 static string GetReplacedString (string typeName, string[] from, string[] to)
1307 for (int i = 0; i < from.Length; ++i)
1308 typeName = typeName.Replace (from [i], to [i]);
1312 // CREATE A STUB DOCUMENTATION FILE
1314 public XmlElement StubType (TypeDefinition type, string output, XmlReader ecmaDocsType)
1316 string typesig = MakeTypeSignature(type);
1317 if (typesig == null) return null; // not publicly visible
1319 XmlDocument doc = new XmlDocument();
1320 XmlElement root = doc.CreateElement("Type");
1321 doc.AppendChild (root);
1323 DoUpdateType2 ("New Type", doc, type, output, true, ecmaDocsType);
1328 private XmlElement CreateSinceNode (XmlDocument doc)
1330 XmlElement s = doc.CreateElement ("since");
1331 s.SetAttribute ("version", since);
1335 // STUBBING/UPDATING FUNCTIONS
1337 public void UpdateType (XmlElement root, TypeDefinition type, XmlReader ecmaDocsType)
1339 root.SetAttribute("Name", GetDocTypeName (type));
1340 root.SetAttribute("FullName", GetDocTypeFullName (type));
1342 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Language", "C#");
1343 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Value", MakeTypeSignature(type));
1345 XmlElement ass = WriteElement(root, "AssemblyInfo");
1346 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1347 if (!no_assembly_versions) {
1348 UpdateAssemblyVersions (root, type, true);
1351 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1352 foreach (var version in versions)
1353 ass.RemoveChild (version);
1355 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1356 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1358 ClearElement(ass, "AssemblyCulture");
1360 // Why-oh-why do we put assembly attributes in each type file?
1361 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1362 // since they're outdated in current docs, and a waste of space.
1363 //MakeAttributes(ass, type.Assembly, true);
1364 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1365 if (assattrs != null)
1366 ass.RemoveChild(assattrs);
1368 NormalizeWhitespace(ass);
1370 if (type.IsGenericType ()) {
1371 MakeTypeParameters (root, type.GenericParameters);
1373 ClearElement(root, "TypeParameters");
1376 if (type.BaseType != null) {
1377 XmlElement basenode = WriteElement(root, "Base");
1379 string basetypename = GetDocTypeFullName (type.BaseType);
1380 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1381 WriteElementText(root, "Base/BaseTypeName", basetypename);
1383 // Document how this type instantiates the generic parameters of its base type
1384 TypeReference origBase = type.BaseType.GetOriginalType ();
1385 if (origBase.IsGenericType ()) {
1386 ClearElement(basenode, "BaseTypeArguments");
1387 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1388 GenericArgumentCollection baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1389 GenericParameterCollection baseGenParams = origBase.GenericParameters;
1390 if (baseGenArgs.Count != baseGenParams.Count)
1391 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1392 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1393 GenericParameter param = baseGenParams [i];
1394 TypeReference value = baseGenArgs [i];
1396 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1397 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1398 bta.AppendChild(arg);
1399 arg.SetAttribute ("TypeParamName", param.Name);
1400 arg.InnerText = GetDocTypeFullName (value);
1404 ClearElement(root, "Base");
1407 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1408 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1409 List<string> interface_names = userInterfaces
1410 .Select (iface => GetDocTypeFullName (iface))
1414 XmlElement interfaces = WriteElement(root, "Interfaces");
1415 interfaces.RemoveAll();
1416 foreach (string iname in interface_names) {
1417 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1418 interfaces.AppendChild(iface);
1419 WriteElementText(iface, "InterfaceName", iname);
1422 ClearElement(root, "Interfaces");
1425 MakeAttributes (root, type.CustomAttributes, false);
1427 if (DocUtils.IsDelegate (type)) {
1428 MakeTypeParameters (root, type.GenericParameters);
1429 MakeParameters(root, type.GetMethod("Invoke").Parameters);
1430 MakeReturnValue(root, type.GetMethod("Invoke"));
1433 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1434 if (ecmaDocsType != null) {
1435 if (ecmaDocsType.Name != "Docs") {
1436 int depth = ecmaDocsType.Depth;
1437 while (ecmaDocsType.Read ()) {
1438 if (ecmaDocsType.Name == "Docs" && ecmaDocsType.Depth == depth + 1)
1442 if (!ecmaDocsType.IsStartElement ("Docs"))
1443 throw new InvalidOperationException ("Found " + ecmaDocsType.Name + "; expecting <Docs/>!");
1444 typeInfo.EcmaDocs = ecmaDocsType;
1446 MakeDocNode (typeInfo);
1448 if (!DocUtils.IsDelegate (type))
1449 WriteElement (root, "Members");
1451 NormalizeWhitespace(root);
1454 static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1456 List<T> l = new List<T> (list);
1461 private void UpdateMember (DocsNodeInfo info)
1463 XmlElement me = (XmlElement) info.Node;
1464 IMemberReference mi = info.Member;
1465 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Language", "C#");
1466 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Value", MakeMemberSignature(mi));
1468 WriteElementText(me, "MemberType", GetMemberType(mi));
1470 if (!no_assembly_versions) {
1471 UpdateAssemblyVersions (me, mi, true);
1474 ClearElement (me, "AssemblyInfo");
1476 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1478 MakeAttributes (me, p.CustomAttributes, false);
1479 MakeReturnValue(me, mi);
1480 if (mi is MethodReference) {
1481 MethodReference mb = (MethodReference) mi;
1482 if (mb.IsGenericMethod ())
1483 MakeTypeParameters (me, mb.GenericParameters);
1485 MakeParameters(me, mi);
1488 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1489 WriteElementText(me, "MemberValue", fieldValue);
1491 info.Node = WriteElement (me, "Docs");
1493 UpdateExtensionMethods (me, info);
1496 static readonly string[] ValidExtensionMembers = {
1505 static readonly string[] ValidExtensionDocMembers = {
1511 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1513 MethodDefinition me = info.Member as MethodDefinition;
1516 if (info.Parameters.Count < 1)
1518 if (!DocUtils.IsExtensionMethod (me))
1521 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1522 XmlNode member = e.CloneNode (true);
1523 em.AppendChild (member);
1524 RemoveExcept (member, ValidExtensionMembers);
1525 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1526 WriteElementText (member, "MemberType", "ExtensionMethod");
1527 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1528 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1529 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1530 member.AppendChild (link);
1531 AddTargets (em, info);
1533 extensionMethods.Add (em);
1536 private static void RemoveExcept (XmlNode node, string[] except)
1540 MyXmlNodeList remove = null;
1541 foreach (XmlNode n in node.ChildNodes) {
1542 if (Array.BinarySearch (except, n.Name) < 0) {
1544 remove = new MyXmlNodeList ();
1549 foreach (XmlNode n in remove)
1550 node.RemoveChild (n);
1553 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1555 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1556 member.PrependChild (targets);
1557 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1558 AppendElementAttributeText (targets, "Target", "Type",
1559 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1562 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1563 ConstraintCollection constraints = gp.Constraints;
1564 if (constraints.Count == 0)
1565 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1567 foreach (TypeReference c in constraints)
1568 AppendElementAttributeText(targets, "Target", "Type",
1569 slashdocFormatter.GetDeclaration (c));
1573 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1576 TypeDefinition type = field.DeclaringType.Resolve ();
1577 if (type != null && type.IsEnum) return false;
1579 if (type != null && type.IsGenericType ()) return false;
1580 if (!field.HasConstant)
1582 if (field.IsLiteral) {
1583 object val = field.Constant;
1584 if (val == null) value = "null";
1585 else if (val is Enum) value = val.ToString();
1586 else if (val is IFormattable) {
1587 value = ((IFormattable)val).ToString();
1589 value = "\"" + value + "\"";
1591 if (value != null && value != "")
1597 // XML HELPER FUNCTIONS
1599 private static XmlElement WriteElement(XmlNode parent, string element) {
1600 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1602 string[] path = element.Split('/');
1603 foreach (string p in path) {
1604 ret = (XmlElement)parent.SelectSingleNode(p);
1607 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1608 ename = ename.Substring(0, ename.IndexOf('['));
1609 ret = parent.OwnerDocument.CreateElement(ename);
1610 parent.AppendChild(ret);
1619 private static void WriteElementText(XmlNode parent, string element, string value) {
1620 XmlElement node = WriteElement(parent, element);
1621 node.InnerText = value;
1624 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1626 XmlElement n = parent.OwnerDocument.CreateElement (element);
1627 parent.AppendChild (n);
1628 n.InnerText = value;
1632 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1634 XmlElement n = parent.OwnerDocument.CreateElement (element);
1635 parent.AppendChild (n);
1636 n.SetAttribute (attribute, value);
1640 private static XmlNode CopyNode (XmlNode source, XmlNode dest)
1642 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1643 dest.AppendChild (copy);
1647 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1648 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1651 node = WriteElement(parent, element);
1652 node.InnerText = value;
1654 private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
1655 XmlElement node = WriteElement(parent, element);
1656 if (node.GetAttribute(attribute) == value) return;
1657 node.SetAttribute(attribute, value);
1659 private static void ClearElement(XmlElement parent, string name) {
1660 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1662 parent.RemoveChild(node);
1665 // DOCUMENTATION HELPER FUNCTIONS
1667 private void MakeDocNode (DocsNodeInfo info)
1669 List<GenericParameter> genericParams = info.GenericParameters;
1670 ParameterDefinitionCollection parameters = info.Parameters;
1671 TypeReference returntype = info.ReturnType;
1672 bool returnisreturn = info.ReturnIsReturn;
1673 XmlElement e = info.Node;
1674 bool addremarks = info.AddRemarks;
1676 WriteElementInitialText(e, "summary", "To be added.");
1678 if (parameters != null) {
1679 string[] values = new string [parameters.Count];
1680 for (int i = 0; i < values.Length; ++i)
1681 values [i] = parameters [i].Name;
1682 UpdateParameters (e, "param", values);
1685 if (genericParams != null) {
1686 string[] values = new string [genericParams.Count];
1687 for (int i = 0; i < values.Length; ++i)
1688 values [i] = genericParams [i].Name;
1689 UpdateParameters (e, "typeparam", values);
1692 string retnodename = null;
1693 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
1694 retnodename = returnisreturn ? "returns" : "value";
1695 string retnodename_other = !returnisreturn ? "returns" : "value";
1697 // If it has a returns node instead of a value node, change its name.
1698 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1699 if (retother != null) {
1700 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1701 foreach (XmlNode node in retother)
1702 retnode.AppendChild(node.CloneNode(true));
1703 e.ReplaceChild(retnode, retother);
1705 WriteElementInitialText(e, retnodename, "To be added.");
1708 ClearElement(e, "returns");
1709 ClearElement(e, "value");
1713 WriteElementInitialText(e, "remarks", "To be added.");
1715 if (exceptions.HasValue && info.Member != null) {
1716 UpdateExceptions (e, info.Member);
1719 if (info.EcmaDocs != null) {
1720 XmlReader r = info.EcmaDocs;
1721 int depth = r.Depth;
1722 r.ReadStartElement ("Docs");
1724 if (r.Name == "Docs") {
1725 if (r.Depth == depth && r.NodeType == XmlNodeType.EndElement)
1728 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
1730 if (!r.IsStartElement ())
1735 string name = r.GetAttribute ("name");
1738 XmlNode doc = e.SelectSingleNode (
1739 r.Name + "[@name='" + name + "']");
1740 string value = r.ReadInnerXml ();
1742 doc.InnerXml = value.Replace ("\r", "");
1749 string name = r.Name;
1750 string cref = r.GetAttribute ("cref");
1753 XmlNode doc = e.SelectSingleNode (
1754 r.Name + "[@cref='" + cref + "']");
1755 string value = r.ReadInnerXml ().Replace ("\r", "");
1757 doc.InnerXml = value;
1759 XmlElement n = e.OwnerDocument.CreateElement (name);
1760 n.SetAttribute ("cref", cref);
1767 string name = r.Name;
1768 string xpath = r.Name;
1769 StringList attributes = new StringList (r.AttributeCount);
1770 if (r.MoveToFirstAttribute ()) {
1772 attributes.Add ("@" + r.Name + "=\"" + r.Value + "\"");
1773 } while (r.MoveToNextAttribute ());
1776 if (attributes.Count > 0) {
1777 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
1779 XmlNode doc = e.SelectSingleNode (xpath);
1780 string value = r.ReadInnerXml ().Replace ("\r", "");
1782 doc.InnerXml = value;
1785 XmlElement n = e.OwnerDocument.CreateElement (name);
1787 foreach (string a in attributes) {
1788 int eq = a.IndexOf ('=');
1789 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
1798 if (info.SlashDocs != null) {
1799 XmlNode elem = info.SlashDocs;
1801 if (elem.SelectSingleNode("summary") != null)
1802 ClearElement(e, "summary");
1803 if (elem.SelectSingleNode("remarks") != null)
1804 ClearElement(e, "remarks");
1805 if (elem.SelectSingleNode("value") != null)
1806 ClearElement(e, "value");
1807 if (retnodename != null && elem.SelectSingleNode(retnodename) != null)
1808 ClearElement(e, retnodename);
1810 foreach (XmlNode child in elem.ChildNodes) {
1811 switch (child.Name) {
1814 XmlAttribute name = child.Attributes ["name"];
1817 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
1819 p2.InnerXml = child.InnerXml;
1824 case "permission": {
1825 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
1828 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
1830 a = e.OwnerDocument.CreateElement (child.Name);
1831 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
1834 a.InnerXml = child.InnerXml;
1838 XmlAttribute cref = child.Attributes ["cref"];
1841 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
1843 a = e.OwnerDocument.CreateElement ("altmember");
1844 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
1850 CopyNode (child, e);
1857 OrderDocsNodes (e, e.ChildNodes);
1858 NormalizeWhitespace(e);
1861 static readonly string[] DocsNodeOrder = {
1862 "typeparam", "param", "summary", "returns", "value", "remarks",
1865 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
1867 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1868 for (int i = 0; i < DocsNodeOrder.Length; ++i) {
1869 for (int j = 0; j < children.Count; ++j) {
1870 XmlNode c = children [j];
1871 if (c.Name == DocsNodeOrder [i]) {
1872 newChildren.Add (c);
1876 if (newChildren.Count >= 0)
1877 docs.PrependChild ((XmlNode) newChildren [0]);
1878 for (int i = 1; i < newChildren.Count; ++i) {
1879 XmlNode prev = (XmlNode) newChildren [i-1];
1880 XmlNode cur = (XmlNode) newChildren [i];
1881 docs.RemoveChild (cur);
1882 docs.InsertAfter (cur, prev);
1887 private void UpdateParameters (XmlElement e, string element, string[] values)
1889 if (values != null) {
1890 XmlNode[] paramnodes = new XmlNode[values.Length];
1892 // Some documentation had param nodes with leading spaces.
1893 foreach (XmlElement paramnode in e.SelectNodes(element)){
1894 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
1897 // If a member has only one parameter, we can track changes to
1898 // the name of the parameter easily.
1899 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
1900 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
1903 bool reinsert = false;
1905 // Pick out existing and still-valid param nodes, and
1906 // create nodes for parameters not in the file.
1907 Hashtable seenParams = new Hashtable();
1908 for (int pi = 0; pi < values.Length; pi++) {
1909 string p = values [pi];
1912 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
1913 if (paramnodes[pi] != null) continue;
1915 XmlElement pe = e.OwnerDocument.CreateElement(element);
1916 pe.SetAttribute("name", p);
1917 pe.InnerText = "To be added.";
1918 paramnodes[pi] = pe;
1922 // Remove parameters that no longer exist and check all params are in the right order.
1924 MyXmlNodeList todelete = new MyXmlNodeList ();
1925 foreach (XmlElement paramnode in e.SelectNodes(element)) {
1926 string name = paramnode.GetAttribute("name");
1927 if (!seenParams.ContainsKey(name)) {
1928 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1929 Warning ("The following param node can only be deleted if the --delete option is given: ");
1930 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
1932 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
1933 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1937 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
1938 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1939 e.ParentNode.Attributes ["MemberName"].Value,
1942 Warning ("\tValue={0}", paramnode.OuterXml);
1944 todelete.Add (paramnode);
1949 if ((int)seenParams[name] != idx)
1955 foreach (XmlNode n in todelete) {
1956 n.ParentNode.RemoveChild (n);
1959 // Re-insert the parameter nodes at the top of the doc section.
1961 for (int pi = values.Length-1; pi >= 0; pi--)
1962 e.PrependChild(paramnodes[pi]);
1964 // Clear all existing param nodes
1965 foreach (XmlNode paramnode in e.SelectNodes(element)) {
1966 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1967 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
1968 Console.WriteLine(paramnode.OuterXml);
1970 paramnode.ParentNode.RemoveChild(paramnode);
1976 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
1978 string existingName = pe.GetAttribute ("name");
1979 pe.SetAttribute ("name", newName);
1980 if (existingName == newName)
1982 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
1983 if (paramref.GetAttribute ("name").Trim () == existingName)
1984 paramref.SetAttribute ("name", newName);
1987 class CrefComparer : XmlNodeComparer {
1989 public CrefComparer ()
1993 public override int Compare (XmlNode x, XmlNode y)
1995 string xType = x.Attributes ["cref"].Value;
1996 string yType = y.Attributes ["cref"].Value;
1997 string xNamespace = GetNamespace (xType);
1998 string yNamespace = GetNamespace (yType);
2000 int c = xNamespace.CompareTo (yNamespace);
2003 return xType.CompareTo (yType);
2006 static string GetNamespace (string type)
2008 int n = type.LastIndexOf ('.');
2010 return type.Substring (0, n);
2011 return string.Empty;
2015 private void UpdateExceptions (XmlNode docs, IMemberReference member)
2017 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
2018 string cref = slashdocFormatter.GetDeclaration (source.Exception);
2019 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
2022 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
2023 e.SetAttribute ("cref", cref);
2024 e.InnerXml = "To be added; from: <see cref=\"" +
2025 string.Join ("\" />, <see cref=\"",
2026 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
2029 docs.AppendChild (e);
2031 SortXmlNodes (docs, docs.SelectNodes ("exception"),
2032 new CrefComparer ());
2035 private static void NormalizeWhitespace(XmlElement e) {
2036 // Remove all text and whitespace nodes from the element so it
2037 // is outputted with nice indentation and no blank lines.
2038 ArrayList deleteNodes = new ArrayList();
2039 foreach (XmlNode n in e)
2040 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2042 foreach (XmlNode n in deleteNodes)
2043 n.ParentNode.RemoveChild(n);
2046 private static bool UpdateAssemblyVersions (XmlElement root, IMemberReference member, bool add)
2048 TypeDefinition type = member as TypeDefinition;
2050 type = member.DeclaringType as TypeDefinition;
2051 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
2054 private static string GetAssemblyVersion (AssemblyDefinition assembly)
2056 return assembly.Name.Version.ToString();
2059 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
2061 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
2063 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2064 root.AppendChild(e);
2066 MyXmlNodeList matches = new MyXmlNodeList (assemblyVersions.Length);
2067 foreach (XmlElement v in e.SelectNodes ("AssemblyVersion")) {
2068 foreach (string sv in assemblyVersions)
2069 if (v.InnerText == sv)
2072 // matches.Count > 0 && add: ignore -- already present
2073 if (matches.Count > 0 && !add) {
2074 foreach (XmlNode c in matches)
2077 else if (matches.Count == 0 && add) {
2078 foreach (string sv in assemblyVersions) {
2079 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2084 // matches.Count == 0 && !add: ignore -- already not present
2086 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2087 SortXmlNodes (e, avs, new VersionComparer ());
2089 return avs.Count != 0;
2092 // FIXME: get TypeReferences instead of string comparison?
2093 private static string[] IgnorableAttributes = {
2094 // Security related attributes
2095 "System.Reflection.AssemblyKeyFileAttribute",
2096 "System.Reflection.AssemblyDelaySignAttribute",
2097 // Present in @RefType
2098 "System.Runtime.InteropServices.OutAttribute",
2099 // For naming the indexer to use when not using indexers
2100 "System.Reflection.DefaultMemberAttribute",
2101 // for decimal constants
2102 "System.Runtime.CompilerServices.DecimalConstantAttribute",
2103 // compiler generated code
2104 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
2105 // more compiler generated code, e.g. iterator methods
2106 "System.Diagnostics.DebuggerHiddenAttribute",
2107 "System.Runtime.CompilerServices.FixedBufferAttribute",
2108 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
2109 // extension methods
2110 "System.Runtime.CompilerServices.ExtensionAttribute",
2113 private void MakeAttributes (XmlElement root, CustomAttributeCollection attributes, bool assemblyAttributes)
2115 if (attributes.Count == 0) {
2116 ClearElement(root, "Attributes");
2121 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2125 e = root.OwnerDocument.CreateElement("Attributes");
2127 foreach (CustomAttribute attribute in attributes.Cast<CustomAttribute> ()
2128 .OrderBy (ca => ca.Constructor.DeclaringType.FullName)) {
2129 if (!attribute.Resolve ()) {
2131 Warning ("warning: could not resolve type {0}.",
2132 attribute.Constructor.DeclaringType.FullName);
2134 TypeDefinition attrType = attribute.Constructor.DeclaringType as TypeDefinition;
2135 if (attrType != null && !IsPublic (attrType))
2137 if (slashdocFormatter.GetName (attribute.Constructor.DeclaringType) == null)
2140 if (Array.IndexOf (IgnorableAttributes, attribute.Constructor.DeclaringType.FullName) >= 0)
2145 StringList fields = new StringList ();
2147 ParameterDefinitionCollection parameters = attribute.Constructor.Parameters;
2148 for (int i = 0; i < attribute.ConstructorParameters.Count; ++i) {
2149 fields.Add (MakeAttributesValueString (
2150 attribute.ConstructorParameters [i],
2151 parameters [i].ParameterType));
2154 (from de in attribute.Fields.Cast<DictionaryEntry> ()
2155 select new { Type=attribute.GetFieldType (de.Key.ToString ()), Name=de.Key, Value=de.Value })
2157 (from de in attribute.Properties.Cast<DictionaryEntry> ()
2158 select new { Type=attribute.GetPropertyType (de.Key.ToString ()), Name=de.Key, Value=de.Value }))
2159 .OrderBy (v => v.Name);
2160 foreach (var d in namedArgs)
2161 fields.Add (string.Format ("{0}={1}", d.Name,
2162 MakeAttributesValueString (d.Value, d.Type)));
2164 string a2 = String.Join(", ", fields.ToArray ());
2165 if (a2 != "") a2 = "(" + a2 + ")";
2167 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2170 string name = attribute.Constructor.DeclaringType.FullName;
2171 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
2172 WriteElementText(ae, "AttributeName", name + a2);
2175 if (b && e.ParentNode == null)
2176 root.AppendChild(e);
2178 ClearElement(root, "Attributes");
2180 NormalizeWhitespace(e);
2183 private static string MakeAttributesValueString (object v, TypeReference valueType)
2187 if (valueType.FullName == "System.Type")
2188 return "typeof(" + v.ToString () + ")";
2189 if (valueType.FullName == "System.String")
2190 return "\"" + v.ToString () + "\"";
2192 return (bool)v ? "true" : "false";
2193 TypeDefinition valueDef = valueType.Resolve ();
2194 if (valueDef == null || !valueDef.IsEnum)
2195 return v.ToString ();
2196 string typename = GetDocTypeFullName (valueType);
2197 var values = GetEnumerationValues (valueDef);
2198 ulong c = Convert.ToUInt64 (v);
2199 if (values.ContainsKey (c))
2200 return typename + "." + values [c];
2201 if (valueDef.CustomAttributes.Cast<CustomAttribute> ()
2202 .Any (ca => ca.Constructor.DeclaringType.FullName == "System.FlagsAttribute")) {
2203 return string.Join (" | ",
2204 (from i in values.Keys
2206 select typename + "." + values [i])
2209 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
2212 private static Dictionary<ulong, string> GetEnumerationValues (TypeDefinition type)
2215 (from f in type.Fields.Cast<FieldDefinition> ()
2216 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
2218 .ToDictionary (f => Convert.ToUInt64 (f.Constant), f => f.Name);
2221 private void MakeParameters (XmlElement root, ParameterDefinitionCollection parameters)
2223 XmlElement e = WriteElement(root, "Parameters");
2225 foreach (ParameterDefinition p in parameters) {
2226 XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
2228 pe.SetAttribute("Name", p.Name);
2229 pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
2230 if (p.ParameterType is ReferenceType) {
2231 if (p.IsOut) pe.SetAttribute("RefType", "out");
2232 else pe.SetAttribute("RefType", "ref");
2234 MakeAttributes (pe, p.CustomAttributes, false);
2238 private void MakeTypeParameters (XmlElement root, GenericParameterCollection typeParams)
2240 if (typeParams == null || typeParams.Count == 0) {
2241 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2243 root.RemoveChild (f);
2246 XmlElement e = WriteElement(root, "TypeParameters");
2248 foreach (GenericParameter t in typeParams) {
2249 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2251 pe.SetAttribute("Name", t.Name);
2252 MakeAttributes (pe, t.CustomAttributes, false);
2253 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2254 ConstraintCollection constraints = t.Constraints;
2255 GenericParameterAttributes attrs = t.Attributes;
2256 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2264 ce = root.OwnerDocument.CreateElement ("Constraints");
2266 pe.AppendChild (ce);
2267 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2268 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2269 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2270 AppendElementText (ce, "ParameterAttribute", "Covariant");
2271 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2272 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2273 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2274 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2275 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2276 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2277 foreach (TypeReference c in constraints) {
2278 TypeDefinition cd = c.Resolve ();
2279 AppendElementText (ce,
2280 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2281 GetDocTypeFullName (c));
2286 private void MakeParameters (XmlElement root, IMemberReference mi)
2288 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2289 MakeParameters (root, ((MethodDefinition)mi).Parameters);
2290 else if (mi is MethodDefinition) {
2291 MethodDefinition mb = (MethodDefinition) mi;
2292 ParameterDefinitionCollection parameters = mb.Parameters;
2293 MakeParameters(root, parameters);
2294 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2295 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2296 p.SetAttribute ("RefType", "this");
2299 else if (mi is PropertyDefinition) {
2300 ParameterDefinitionCollection parameters = ((PropertyDefinition)mi).Parameters;
2301 if (parameters.Count > 0)
2302 MakeParameters(root, parameters);
2306 else if (mi is FieldDefinition) return;
2307 else if (mi is EventDefinition) return;
2308 else throw new ArgumentException();
2311 private static string GetDocParameterType (TypeReference type)
2313 return GetDocTypeFullName (type).Replace ("@", "&");
2316 private void MakeReturnValue (XmlElement root, TypeReference type, CustomAttributeCollection attributes)
2318 XmlElement e = WriteElement(root, "ReturnValue");
2320 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2321 if (attributes != null)
2322 MakeAttributes(e, attributes, false);
2325 private void MakeReturnValue (XmlElement root, IMemberReference mi)
2327 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2329 else if (mi is MethodDefinition)
2330 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType.ReturnType, ((MethodDefinition)mi).ReturnType.CustomAttributes);
2331 else if (mi is PropertyDefinition)
2332 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
2333 else if (mi is FieldDefinition)
2334 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
2335 else if (mi is EventDefinition)
2336 MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
2338 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2341 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2343 IMemberReference mi = info.Member;
2344 if (mi is TypeDefinition) return null;
2346 string sigs = MakeMemberSignature(mi);
2347 if (sigs == null) return null; // not publicly visible
2349 // no documentation for property/event accessors. Is there a better way of doing this?
2350 if (mi.Name.StartsWith("get_")) return null;
2351 if (mi.Name.StartsWith("set_")) return null;
2352 if (mi.Name.StartsWith("add_")) return null;
2353 if (mi.Name.StartsWith("remove_")) return null;
2354 if (mi.Name.StartsWith("raise_")) return null;
2356 XmlElement me = doc.CreateElement("Member");
2357 me.SetAttribute("MemberName", GetMemberName (mi));
2362 if (since != null) {
2363 XmlNode docs = me.SelectSingleNode("Docs");
2364 docs.AppendChild (CreateSinceNode (doc));
2370 private static string GetMemberName (IMemberReference mi)
2372 MethodDefinition mb = mi as MethodDefinition;
2374 PropertyDefinition pi = mi as PropertyDefinition;
2377 return DocUtils.GetPropertyName (pi);
2379 StringBuilder sb = new StringBuilder (mi.Name.Length);
2380 if (!DocUtils.IsExplicitlyImplemented (mb))
2381 sb.Append (mi.Name);
2383 TypeReference iface;
2384 MethodReference ifaceMethod;
2385 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2386 sb.Append (GetDocTypeFullName (iface));
2388 sb.Append (ifaceMethod.Name);
2390 if (mb.IsGenericMethod ()) {
2391 GenericParameterCollection typeParams = mb.GenericParameters;
2392 if (typeParams.Count > 0) {
2394 sb.Append (typeParams [0].Name);
2395 for (int i = 1; i < typeParams.Count; ++i)
2396 sb.Append (",").Append (typeParams [i].Name);
2400 return sb.ToString ();
2403 private static int CountChars (string s, char c)
2406 for (int i = 0; i < s.Length; ++i) {
2413 /// SIGNATURE GENERATION FUNCTIONS
2415 static string MakeTypeSignature (TypeReference type)
2417 return csharpFormatter.GetDeclaration (type);
2420 static string MakeMemberSignature (IMemberReference mi)
2422 return csharpFullFormatter.GetDeclaration (mi);
2425 static string GetMemberType (IMemberReference mi)
2427 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2428 return "Constructor";
2429 if (mi is MethodDefinition)
2431 if (mi is PropertyDefinition)
2433 if (mi is FieldDefinition)
2435 if (mi is EventDefinition)
2437 throw new ArgumentException();
2440 private static string GetDocTypeName (TypeReference type)
2442 return docTypeFormatter.GetName (type);
2445 private static string GetDocTypeFullName (TypeReference type)
2447 return DocTypeFullMemberFormatter.Default.GetName (type);
2450 class DocsNodeInfo {
2451 public DocsNodeInfo (XmlElement node)
2456 public DocsNodeInfo (XmlElement node, TypeDefinition type)
2462 public DocsNodeInfo (XmlElement node, IMemberReference member)
2465 SetMemberInfo (member);
2468 void SetType (TypeDefinition type)
2471 throw new ArgumentNullException ("type");
2472 GenericParameters = new List<GenericParameter> (type.GenericParameters.Cast<GenericParameter> ());
2473 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
2474 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
2475 for (int i = 0; i < declTypes.Count - 1; ++i) {
2476 int remove = System.Math.Min (maxGenArgs,
2477 DocUtils.GetGenericArgumentCount (declTypes [i]));
2478 maxGenArgs -= remove;
2479 while (remove-- > 0)
2480 GenericParameters.RemoveAt (0);
2482 if (DocUtils.IsDelegate (type)) {
2483 Parameters = type.GetMethod("Invoke").Parameters;
2484 ReturnType = type.GetMethod("Invoke").ReturnType.ReturnType;
2486 SetSlashDocs (type);
2489 void SetMemberInfo (IMemberReference member)
2492 throw new ArgumentNullException ("member");
2493 ReturnIsReturn = true;
2497 if (member is MethodReference ) {
2498 MethodReference mr = (MethodReference) member;
2499 Parameters = mr.Parameters;
2500 if (mr.IsGenericMethod ()) {
2501 GenericParameters = new List<GenericParameter> (mr.GenericParameters.Cast<GenericParameter> ());
2504 else if (member is PropertyDefinition) {
2505 Parameters = ((PropertyDefinition) member).Parameters;
2508 if (member is MethodDefinition) {
2509 ReturnType = ((MethodDefinition) member).ReturnType.ReturnType;
2510 } else if (member is PropertyDefinition) {
2511 ReturnType = ((PropertyDefinition) member).PropertyType;
2512 ReturnIsReturn = false;
2515 // no remarks section for enum members
2516 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
2518 SetSlashDocs (member);
2521 private void SetSlashDocs (IMemberReference member)
2523 if (slashdocs == null)
2526 string slashdocsig = slashdocFormatter.GetDeclaration (member);
2527 if (slashdocsig != null)
2528 SlashDocs = slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
2531 public TypeReference ReturnType;
2532 public List<GenericParameter> GenericParameters;
2533 public ParameterDefinitionCollection Parameters;
2534 public bool ReturnIsReturn;
2535 public XmlElement Node;
2536 public bool AddRemarks = true;
2537 public XmlNode SlashDocs;
2538 public XmlReader EcmaDocs;
2539 public IMemberReference Member;
2542 static string GetXPathForMember (DocumentationMember member)
2544 StringBuilder xpath = new StringBuilder ();
2545 xpath.Append ("//Members/Member[@MemberName=\"")
2546 .Append (member.MemberName)
2548 if (member.Parameters != null && member.Parameters.Count > 0) {
2549 xpath.Append ("/Parameters[count(Parameter) = ")
2550 .Append (member.Parameters.Count);
2551 for (int i = 0; i < member.Parameters.Count; ++i) {
2552 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2553 xpath.Append (member.Parameters [i]);
2554 xpath.Append ("\"");
2556 xpath.Append ("]/..");
2558 return xpath.ToString ();
2561 public static string GetXPathForMember (XPathNavigator member)
2563 StringBuilder xpath = new StringBuilder ();
2564 xpath.Append ("//Type[@FullName=\"")
2565 .Append (member.SelectSingleNode ("../../@FullName").Value)
2567 xpath.Append ("Members/Member[@MemberName=\"")
2568 .Append (member.SelectSingleNode ("@MemberName").Value)
2570 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2571 if (parameters.Count > 0) {
2572 xpath.Append ("/Parameters[count(Parameter) = ")
2573 .Append (parameters.Count);
2575 while (parameters.MoveNext ()) {
2577 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2578 xpath.Append (parameters.Current.Value);
2579 xpath.Append ("\"");
2581 xpath.Append ("]/..");
2583 return xpath.ToString ();
2586 public static string GetXPathForMember (IMemberReference member)
2588 StringBuilder xpath = new StringBuilder ();
2589 xpath.Append ("//Type[@FullName=\"")
2590 .Append (member.DeclaringType.FullName)
2592 xpath.Append ("Members/Member[@MemberName=\"")
2593 .Append (GetMemberName (member))
2596 ParameterDefinitionCollection parameters = null;
2597 if (member is MethodDefinition)
2598 parameters = ((MethodDefinition) member).Parameters;
2599 else if (member is PropertyDefinition) {
2600 parameters = ((PropertyDefinition) member).Parameters;
2602 if (parameters != null && parameters.Count > 0) {
2603 xpath.Append ("/Parameters[count(Parameter) = ")
2604 .Append (parameters.Count);
2605 for (int i = 0; i < parameters.Count; ++i) {
2606 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2607 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2608 xpath.Append ("\"");
2610 xpath.Append ("]/..");
2612 return xpath.ToString ();
2616 static class CecilExtensions {
2617 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type)
2619 foreach (var c in type.Constructors)
2620 yield return (IMemberReference) c;
2621 foreach (var e in type.Events)
2622 yield return (IMemberReference) e;
2623 foreach (var f in type.Fields)
2624 yield return (IMemberReference) f;
2625 foreach (var m in type.Methods)
2626 yield return (IMemberReference) m;
2627 foreach (var t in type.NestedTypes)
2628 yield return (IMemberReference) t;
2629 foreach (var p in type.Properties)
2630 yield return (IMemberReference) p;
2633 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type, string member)
2635 return GetMembers (type).Where (m => m.Name == member);
2638 public static IMemberReference GetMember (this TypeDefinition type, string member)
2640 return GetMembers (type, member).EnsureZeroOrOne ();
2643 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2645 if (source.Count () > 1)
2646 throw new InvalidOperationException ("too many matches");
2647 return source.FirstOrDefault ();
2650 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2652 return type.Methods.Cast<MethodDefinition> ()
2653 .Where (m => m.Name == method)
2654 .EnsureZeroOrOne ();
2657 public static IEnumerable<IMemberReference> GetDefaultMembers (this TypeReference type)
2659 TypeDefinition def = type as TypeDefinition;
2661 return new IMemberReference [0];
2662 CustomAttribute defMemberAttr = type.CustomAttributes.Cast<CustomAttribute> ()
2663 .Where (c => c.Constructor.DeclaringType.FullName == "System.Reflection.DefaultMemberAttribute")
2665 if (defMemberAttr == null)
2666 return new IMemberReference [0];
2667 string name = (string) defMemberAttr.ConstructorParameters [0];
2668 return def.Properties.Cast<PropertyDefinition> ()
2669 .Where (p => p.Name == name)
2670 .Select (p => (IMemberReference) p);
2673 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2675 return assembly.Modules.Cast<ModuleDefinition> ()
2676 .SelectMany (md => md.Types.Cast<TypeDefinition> ());
2679 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2681 return GetTypes (assembly)
2682 .Where (td => td.FullName == type)
2683 .EnsureZeroOrOne ();
2686 public static bool IsGenericType (this TypeReference type)
2688 return type.GenericParameters.Count > 0;
2691 public static bool IsGenericMethod (this MethodReference method)
2693 return method.GenericParameters.Count > 0;
2696 public static IMemberReference Resolve (this IMemberReference member)
2698 EventReference er = member as EventReference;
2700 return er.Resolve ();
2701 FieldReference fr = member as FieldReference;
2703 return fr.Resolve ();
2704 MethodReference mr = member as MethodReference;
2706 return mr.Resolve ();
2707 PropertyReference pr = member as PropertyReference;
2709 return pr.Resolve ();
2710 TypeReference tr = member as TypeReference;
2712 return tr.Resolve ();
2713 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2717 static class DocUtils {
2718 public static bool IsExplicitlyImplemented (MethodDefinition method)
2720 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2723 public static string GetTypeDotMember (string name)
2725 int startType, startMethod;
2726 startType = startMethod = -1;
2727 for (int i = 0; i < name.Length; ++i) {
2728 if (name [i] == '.') {
2729 startType = startMethod;
2733 return name.Substring (startType+1);
2736 public static string GetMember (string name)
2738 int i = name.LastIndexOf ('.');
2741 return name.Substring (i+1);
2744 public static void GetInfoForExplicitlyImplementedMethod (
2745 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2749 if (method.Overrides.Count != 1)
2750 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2751 iface = method.Overrides [0].DeclaringType;
2752 ifaceMethod = method.Overrides [0];
2755 public static string GetPropertyName (PropertyDefinition pi)
2757 // Issue: (g)mcs-generated assemblies that explicitly implement
2758 // properties don't specify the full namespace, just the
2759 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2760 MethodDefinition method = pi.GetMethod;
2762 method = pi.SetMethod;
2763 if (!IsExplicitlyImplemented (method))
2766 // Need to determine appropriate namespace for this member.
2767 TypeReference iface;
2768 MethodReference ifaceMethod;
2769 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2770 return string.Join (".", new string[]{
2771 DocTypeFullMemberFormatter.Default.GetName (iface),
2772 GetMember (pi.Name)});
2775 public static string GetNamespace (TypeReference type)
2777 if (type.GetOriginalType ().IsNested)
2778 type = type.GetOriginalType ();
2779 while (type != null && type.IsNested)
2780 type = type.DeclaringType;
2782 return string.Empty;
2783 return type.Namespace;
2786 public static string PathCombine (string dir, string path)
2792 return Path.Combine (dir, path);
2795 public static bool IsExtensionMethod (MethodDefinition method)
2798 method.CustomAttributes.Cast<CustomAttribute> ()
2799 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2801 method.DeclaringType.CustomAttributes.Cast<CustomAttribute> ()
2802 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2806 public static bool IsDelegate (TypeDefinition type)
2808 TypeReference baseRef = type.BaseType;
2809 if (baseRef == null)
2811 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2812 baseRef.FullName == "System.MulticastDelegate";
2815 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2817 List<TypeReference> decls = new List<TypeReference> ();
2819 while (type.DeclaringType != null) {
2820 decls.Add (type.DeclaringType);
2821 type = type.DeclaringType;
2827 public static int GetGenericArgumentCount (TypeReference type)
2829 GenericInstanceType inst = type as GenericInstanceType;
2831 ? inst.GenericArguments.Count
2832 : type.GenericParameters.Count;
2835 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
2837 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
2838 List<TypeReference> userInterfaces = new List<TypeReference> ();
2839 foreach (TypeReference iface in type.Interfaces) {
2840 TypeReference lookup = iface.Resolve () ?? iface;
2841 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
2842 userInterfaces.Add (iface);
2844 return userInterfaces;
2847 private static string GetQualifiedTypeName (TypeReference type)
2849 return "[" + type.Scope.Name + "]" + type.FullName;
2852 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
2854 HashSet<string> inheritedInterfaces = new HashSet<string> ();
2855 Action<TypeDefinition> a = null;
2857 if (t == null) return;
2858 foreach (TypeReference r in t.Interfaces) {
2859 inheritedInterfaces.Add (GetQualifiedTypeName (r));
2863 TypeReference baseRef = type.BaseType;
2864 while (baseRef != null) {
2865 TypeDefinition baseDef = baseRef.Resolve ();
2866 if (baseDef != null) {
2868 baseRef = baseDef.BaseType;
2873 foreach (TypeReference r in type.Interfaces)
2875 return inheritedInterfaces;
2879 class DocumentationMember {
2880 public StringToStringMap MemberSignatures = new StringToStringMap ();
2881 public string ReturnType;
2882 public StringList Parameters;
2883 public string MemberName;
2884 public string MemberType;
2886 public DocumentationMember (XmlReader reader)
2888 MemberName = reader.GetAttribute ("MemberName");
2889 int depth = reader.Depth;
2891 StringList p = new StringList ();
2893 if (reader.NodeType != XmlNodeType.Element)
2895 switch (reader.Name) {
2896 case "MemberSignature":
2897 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
2900 MemberType = reader.ReadElementString ();
2903 if (reader.Depth == depth + 2)
2904 ReturnType = reader.ReadElementString ();
2907 if (reader.Depth == depth + 2)
2908 p.Add (reader.GetAttribute ("Type"));
2911 if (reader.Depth == depth + 1)
2915 } while (go && reader.Read () && reader.Depth >= depth);
2921 public DocumentationMember (XmlNode node)
2923 MemberName = node.Attributes ["MemberName"].Value;
2924 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
2925 XmlAttribute l = n.Attributes ["Language"];
2926 XmlAttribute v = n.Attributes ["Value"];
2927 if (l != null && v != null)
2928 MemberSignatures [l.Value] = v.Value;
2930 MemberType = node.SelectSingleNode ("MemberType").InnerText;
2931 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
2933 ReturnType = rt.InnerText;
2934 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
2936 Parameters = new StringList (p.Count);
2937 for (int i = 0; i < p.Count; ++i)
2938 Parameters.Add (p [i].Attributes ["Type"].Value);
2943 public enum MemberFormatterState {
2946 WithinGenericTypeContainer,
2949 public abstract class MemberFormatter {
2950 public virtual string GetName (IMemberReference member)
2952 TypeReference type = member as TypeReference;
2954 return GetTypeName (type);
2955 MethodReference method = member as MethodReference;
2956 if (method != null && method.Name == ".ctor") // method.IsConstructor
2957 return GetConstructorName (method);
2959 return GetMethodName (method);
2960 PropertyReference prop = member as PropertyReference;
2962 return GetPropertyName (prop);
2963 FieldReference field = member as FieldReference;
2965 return GetFieldName (field);
2966 EventReference e = member as EventReference;
2968 return GetEventName (e);
2969 throw new NotSupportedException ("Can't handle: " +
2970 (member == null ? "null" : member.GetType().ToString()));
2973 protected virtual string GetTypeName (TypeReference type)
2976 throw new ArgumentNullException ("type");
2977 return _AppendTypeName (new StringBuilder (type.Name.Length), type).ToString ();
2980 protected virtual char[] ArrayDelimeters {
2981 get {return new char[]{'[', ']'};}
2984 protected virtual MemberFormatterState MemberFormatterState { get; set; }
2986 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type)
2988 if (type is ArrayType) {
2989 TypeSpecification spec = type as TypeSpecification;
2990 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
2991 .Append (ArrayDelimeters [0]);
2992 var origState = MemberFormatterState;
2993 MemberFormatterState = MemberFormatterState.WithinArray;
2994 ArrayType array = (ArrayType) type;
2995 int rank = array.Rank;
2997 buf.Append (new string (',', rank-1));
2998 MemberFormatterState = origState;
2999 return buf.Append (ArrayDelimeters [1]);
3001 if (type is ReferenceType) {
3002 return AppendRefTypeName (buf, type);
3004 if (type is PointerType) {
3005 return AppendPointerTypeName (buf, type);
3007 AppendNamespace (buf, type);
3008 if (type is GenericParameter) {
3009 return AppendTypeName (buf, type);
3011 GenericInstanceType genInst = type as GenericInstanceType;
3012 if (type.GenericParameters.Count == 0 &&
3013 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3014 return AppendFullTypeName (buf, type);
3016 return AppendGenericType (buf, type);
3019 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3021 string ns = DocUtils.GetNamespace (type);
3022 if (ns != null && ns.Length > 0)
3023 buf.Append (ns).Append ('.');
3027 private StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type)
3029 if (type.DeclaringType != null)
3030 AppendFullTypeName (buf, type.DeclaringType).Append (NestedTypeSeparator);
3031 return AppendTypeName (buf, type);
3034 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3036 return AppendTypeName (buf, type.Name);
3039 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3041 int n = typename.IndexOf ("`");
3043 return buf.Append (typename.Substring (0, n));
3044 return buf.Append (typename);
3047 protected virtual string RefTypeModifier {
3051 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type)
3053 TypeSpecification spec = type as TypeSpecification;
3054 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3055 .Append (RefTypeModifier);
3058 protected virtual string PointerModifier {
3062 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type)
3064 TypeSpecification spec = type as TypeSpecification;
3065 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3066 .Append (PointerModifier);
3069 protected virtual char[] GenericTypeContainer {
3070 get {return new char[]{'<', '>'};}
3073 protected virtual char NestedTypeSeparator {
3077 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3079 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3080 type is GenericInstanceType ? type.GetOriginalType () : type);
3081 List<TypeReference> genArgs = GetGenericArguments (type);
3084 bool insertNested = false;
3085 foreach (var decl in decls) {
3086 TypeReference declDef = decl.Resolve () ?? decl;
3088 buf.Append (NestedTypeSeparator);
3090 insertNested = true;
3091 AppendTypeName (buf, declDef);
3092 int ac = DocUtils.GetGenericArgumentCount (declDef);
3096 buf.Append (GenericTypeContainer [0]);
3097 var origState = MemberFormatterState;
3098 MemberFormatterState = MemberFormatterState.WithinGenericTypeContainer;
3099 _AppendTypeName (buf, genArgs [argIdx++]);
3100 for (int i = 1; i < c; ++i)
3101 _AppendTypeName (buf.Append (","), genArgs [argIdx++]);
3102 MemberFormatterState = origState;
3103 buf.Append (GenericTypeContainer [1]);
3109 private List<TypeReference> GetGenericArguments (TypeReference type)
3111 var args = new List<TypeReference> ();
3112 GenericInstanceType inst = type as GenericInstanceType;
3114 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
3116 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
3120 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3125 protected virtual string GetConstructorName (MethodReference constructor)
3127 return constructor.Name;
3130 protected virtual string GetMethodName (MethodReference method)
3135 protected virtual string GetPropertyName (PropertyReference property)
3137 return property.Name;
3140 protected virtual string GetFieldName (FieldReference field)
3145 protected virtual string GetEventName (EventReference e)
3150 public virtual string GetDeclaration (IMemberReference member)
3153 throw new ArgumentNullException ("member");
3154 TypeDefinition type = member as TypeDefinition;
3156 return GetTypeDeclaration (type);
3157 MethodDefinition method = member as MethodDefinition;
3158 if (method != null && method.IsConstructor)
3159 return GetConstructorDeclaration (method);
3161 return GetMethodDeclaration (method);
3162 PropertyDefinition prop = member as PropertyDefinition;
3164 return GetPropertyDeclaration (prop);
3165 FieldDefinition field = member as FieldDefinition;
3167 return GetFieldDeclaration (field);
3168 EventDefinition e = member as EventDefinition;
3170 return GetEventDeclaration (e);
3171 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3174 protected virtual string GetTypeDeclaration (TypeDefinition type)
3177 throw new ArgumentNullException ("type");
3178 StringBuilder buf = new StringBuilder (type.Name.Length);
3179 _AppendTypeName (buf, type);
3180 AppendGenericTypeConstraints (buf, type);
3181 return buf.ToString ();
3184 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
3186 return GetConstructorName (constructor);
3189 protected virtual string GetMethodDeclaration (MethodDefinition method)
3191 // Special signature for destructors.
3192 if (method.Name == "Finalize" && method.Parameters.Count == 0)
3193 return GetFinalizerName (method);
3195 StringBuilder buf = new StringBuilder ();
3197 AppendVisibility (buf, method);
3198 if (buf.Length == 0 &&
3199 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3202 AppendModifiers (buf, method);
3204 if (buf.Length != 0)
3206 buf.Append (GetName (method.ReturnType.ReturnType)).Append (" ");
3208 AppendMethodName (buf, method);
3209 AppendGenericMethod (buf, method).Append (" ");
3210 AppendParameters (buf, method, method.Parameters);
3211 AppendGenericMethodConstraints (buf, method);
3212 return buf.ToString ();
3215 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3217 return buf.Append (method.Name);
3220 protected virtual string GetFinalizerName (MethodDefinition method)
3225 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3230 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3235 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3240 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3245 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3250 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
3252 return GetPropertyName (property);
3255 protected virtual string GetFieldDeclaration (FieldDefinition field)
3257 return GetFieldName (field);
3260 protected virtual string GetEventDeclaration (EventDefinition e)
3262 return GetEventName (e);
3266 class CSharpFullMemberFormatter : MemberFormatter {
3268 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3270 string ns = DocUtils.GetNamespace (type);
3271 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
3272 buf.Append (ns).Append ('.');
3276 private string GetCSharpType (string t)
3279 case "System.Byte": return "byte";
3280 case "System.SByte": return "sbyte";
3281 case "System.Int16": return "short";
3282 case "System.Int32": return "int";
3283 case "System.Int64": return "long";
3285 case "System.UInt16": return "ushort";
3286 case "System.UInt32": return "uint";
3287 case "System.UInt64": return "ulong";
3289 case "System.Single": return "float";
3290 case "System.Double": return "double";
3291 case "System.Decimal": return "decimal";
3292 case "System.Boolean": return "bool";
3293 case "System.Char": return "char";
3294 case "System.Void": return "void";
3295 case "System.String": return "string";
3296 case "System.Object": return "object";
3301 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3303 if (type is GenericParameter)
3304 return AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
3305 string t = type.FullName;
3306 if (!t.StartsWith ("System.")) {
3307 return base.AppendTypeName (buf, type);
3310 string s = GetCSharpType (t);
3312 return buf.Append (s);
3314 return base.AppendTypeName (buf, type);
3317 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
3319 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeContainer)
3321 GenericParameterAttributes attrs = type.Attributes;
3322 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
3323 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
3327 buf.Append ("out ");
3331 protected override string GetTypeDeclaration (TypeDefinition type)
3333 string visibility = GetTypeVisibility (type.Attributes);
3334 if (visibility == null)
3337 StringBuilder buf = new StringBuilder ();
3339 buf.Append (visibility);
3342 MemberFormatter full = new CSharpFullMemberFormatter ();
3344 if (DocUtils.IsDelegate (type)) {
3345 buf.Append("delegate ");
3346 MethodDefinition invoke = type.GetMethod ("Invoke");
3347 buf.Append (full.GetName (invoke.ReturnType.ReturnType)).Append (" ");
3348 buf.Append (GetName (type));
3349 AppendParameters (buf, invoke, invoke.Parameters);
3350 AppendGenericTypeConstraints (buf, type);
3353 return buf.ToString();
3356 if (type.IsAbstract && !type.IsInterface)
3357 buf.Append("abstract ");
3358 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
3359 buf.Append("sealed ");
3360 buf.Replace ("abstract sealed", "static");
3362 buf.Append (GetTypeKind (type));
3364 buf.Append (GetCSharpType (type.FullName) == null
3369 TypeReference basetype = type.BaseType;
3370 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
3373 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
3374 .Select (iface => full.GetName (iface))
3378 if (basetype != null || interface_names.Count > 0)
3381 if (basetype != null) {
3382 buf.Append (full.GetName (basetype));
3383 if (interface_names.Count > 0)
3387 for (int i = 0; i < interface_names.Count; i++){
3390 buf.Append (interface_names [i]);
3392 AppendGenericTypeConstraints (buf, type);
3395 return buf.ToString ();
3398 static string GetTypeKind (TypeDefinition t)
3404 if (t.IsClass || t.FullName == "System.Enum")
3408 throw new ArgumentException(t.FullName);
3411 static string GetTypeVisibility (TypeAttributes ta)
3413 switch (ta & TypeAttributes.VisibilityMask) {
3414 case TypeAttributes.Public:
3415 case TypeAttributes.NestedPublic:
3418 case TypeAttributes.NestedFamily:
3419 case TypeAttributes.NestedFamORAssem:
3427 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3429 if (type.GenericParameters.Count == 0)
3431 return AppendConstraints (buf, type.GenericParameters);
3434 private StringBuilder AppendConstraints (StringBuilder buf, GenericParameterCollection genArgs)
3436 foreach (GenericParameter genArg in genArgs) {
3437 GenericParameterAttributes attrs = genArg.Attributes;
3438 ConstraintCollection constraints = genArg.Constraints;
3439 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
3442 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
3443 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
3444 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
3447 if (!isref && !isvt && !isnew && constraints.Count == 0)
3449 buf.Append (" where ").Append (genArg.Name).Append (" : ");
3451 buf.Append ("class");
3455 buf.Append ("struct");
3458 if (constraints.Count > 0 && !isvt) {
3461 buf.Append (GetTypeName (constraints [0]));
3462 for (int i = 1; i < constraints.Count; ++i)
3463 buf.Append (", ").Append (GetTypeName (constraints [i]));
3465 if (isnew && !isvt) {
3468 buf.Append ("new()");
3474 protected override string GetConstructorDeclaration (MethodDefinition constructor)
3476 StringBuilder buf = new StringBuilder ();
3477 AppendVisibility (buf, constructor);
3478 if (buf.Length == 0)
3482 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
3483 AppendParameters (buf, constructor, constructor.Parameters);
3486 return buf.ToString ();
3489 protected override string GetMethodDeclaration (MethodDefinition method)
3491 string decl = base.GetMethodDeclaration (method);
3497 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3499 if (DocUtils.IsExplicitlyImplemented (method)) {
3500 TypeReference iface;
3501 MethodReference ifaceMethod;
3502 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3503 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3505 .Append (ifaceMethod.Name);
3507 return base.AppendMethodName (buf, method);
3510 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3512 if (method.GenericParameters.Count == 0)
3514 return AppendConstraints (buf, method.GenericParameters);
3517 protected override string RefTypeModifier {
3521 protected override string GetFinalizerName (MethodDefinition method)
3523 return "~" + method.DeclaringType.Name + " ()";
3526 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3530 if (method.IsPublic)
3531 return buf.Append ("public");
3532 if (method.IsFamily || method.IsFamilyOrAssembly)
3533 return buf.Append ("protected");
3537 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3539 string modifiers = String.Empty;
3540 if (method.IsStatic) modifiers += " static";
3541 if (method.IsVirtual && !method.IsAbstract) {
3542 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3543 else modifiers += " override";
3545 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
3546 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
3547 if (method.IsFinal) modifiers += " sealed";
3548 if (modifiers == " virtual sealed") modifiers = "";
3550 return buf.Append (modifiers);
3553 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3555 if (method.IsGenericMethod ()) {
3556 GenericParameterCollection args = method.GenericParameters;
3557 if (args.Count > 0) {
3559 buf.Append (args [0].Name);
3560 for (int i = 1; i < args.Count; ++i)
3561 buf.Append (",").Append (args [i].Name);
3568 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3570 return AppendParameters (buf, method, parameters, '(', ')');
3573 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters, char begin, char end)
3577 if (parameters.Count > 0) {
3578 if (DocUtils.IsExtensionMethod (method))
3579 buf.Append ("this ");
3580 AppendParameter (buf, parameters [0]);
3581 for (int i = 1; i < parameters.Count; ++i) {
3583 AppendParameter (buf, parameters [i]);
3587 return buf.Append (end);
3590 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
3592 if (parameter.ParameterType is ReferenceType) {
3593 if (parameter.IsOut)
3594 buf.Append ("out ");
3596 buf.Append ("ref ");
3598 buf.Append (GetName (parameter.ParameterType)).Append (" ");
3599 return buf.Append (parameter.Name);
3602 protected override string GetPropertyDeclaration (PropertyDefinition property)
3604 MethodDefinition method;
3606 string get_visible = null;
3607 if ((method = property.GetMethod) != null &&
3608 (DocUtils.IsExplicitlyImplemented (method) ||
3609 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3610 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3611 string set_visible = null;
3612 if ((method = property.SetMethod) != null &&
3613 (DocUtils.IsExplicitlyImplemented (method) ||
3614 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3615 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3617 if ((set_visible == null) && (get_visible == null))
3621 StringBuilder buf = new StringBuilder ();
3622 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
3623 buf.Append (visibility = get_visible);
3624 else if (set_visible != null && get_visible == null)
3625 buf.Append (visibility = set_visible);
3627 buf.Append (visibility = "public");
3629 // Pick an accessor to use for static/virtual/override/etc. checks.
3630 method = property.SetMethod;
3632 method = property.GetMethod;
3634 string modifiers = String.Empty;
3635 if (method.IsStatic) modifiers += " static";
3636 if (method.IsVirtual && !method.IsAbstract) {
3637 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
3638 modifiers += " virtual";
3640 modifiers += " override";
3642 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
3643 if (method.IsAbstract && !declDef.IsInterface)
3644 modifiers += " abstract";
3646 modifiers += " sealed";
3647 if (modifiers == " virtual sealed")
3649 buf.Append (modifiers).Append (' ');
3651 buf.Append (GetName (property.PropertyType)).Append (' ');
3653 IEnumerable<IMemberReference> defs = property.DeclaringType.GetDefaultMembers ();
3654 string name = property.Name;
3655 foreach (IMemberReference mi in defs) {
3656 if (mi == property) {
3661 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
3663 if (property.Parameters.Count != 0) {
3664 AppendParameters (buf, method, property.Parameters, '[', ']');
3668 if (set_visible != null) {
3669 if (set_visible != visibility)
3670 buf.Append (' ').Append (set_visible);
3671 buf.Append (" set;");
3673 if (get_visible != null) {
3674 if (get_visible != visibility)
3675 buf.Append (' ').Append (get_visible);
3676 buf.Append (" get;");
3680 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
3683 protected override string GetFieldDeclaration (FieldDefinition field)
3685 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
3686 if (declType.IsEnum && field.Name == "value__")
3687 return null; // This member of enums aren't documented.
3689 StringBuilder buf = new StringBuilder ();
3690 AppendFieldVisibility (buf, field);
3691 if (buf.Length == 0)
3694 if (declType.IsEnum)
3697 if (field.IsStatic && !field.IsLiteral)
3698 buf.Append (" static");
3699 if (field.IsInitOnly)
3700 buf.Append (" readonly");
3701 if (field.IsLiteral)
3702 buf.Append (" const");
3704 buf.Append (' ').Append (GetName (field.FieldType)).Append (' ');
3705 buf.Append (field.Name);
3706 AppendFieldValue (buf, field);
3709 return buf.ToString ();
3712 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
3715 return buf.Append ("public");
3716 if (field.IsFamily || field.IsFamilyOrAssembly)
3717 return buf.Append ("protected");
3721 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
3723 // enums have a value__ field, which we ignore
3724 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
3725 field.DeclaringType.IsGenericType ())
3727 if (field.HasConstant && field.IsLiteral) {
3730 val = field.Constant;
3735 buf.Append (" = ").Append ("null");
3736 else if (val is Enum)
3737 buf.Append (" = ").Append (val.ToString ());
3738 else if (val is IFormattable) {
3739 string value = ((IFormattable)val).ToString();
3741 value = "\"" + value + "\"";
3742 buf.Append (" = ").Append (value);
3748 protected override string GetEventDeclaration (EventDefinition e)
3750 StringBuilder buf = new StringBuilder ();
3751 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
3755 AppendModifiers (buf, e.AddMethod);
3757 buf.Append (" event ");
3758 buf.Append (GetName (e.EventType)).Append (' ');
3759 buf.Append (e.Name).Append (';');
3761 return buf.ToString ();
3765 class CSharpMemberFormatter : CSharpFullMemberFormatter {
3766 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3772 class DocTypeFullMemberFormatter : MemberFormatter {
3773 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
3775 protected override char NestedTypeSeparator {
3780 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
3781 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3787 class SlashDocMemberFormatter : MemberFormatter {
3789 protected override char[] GenericTypeContainer {
3790 get {return new char[]{'{', '}'};}
3793 private bool AddTypeCount = true;
3795 private TypeReference genDeclType;
3796 private MethodReference genDeclMethod;
3798 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3800 if (type is GenericParameter) {
3802 if (genDeclType != null) {
3803 GenericParameterCollection genArgs = genDeclType.GenericParameters;
3804 for (int i = 0; i < genArgs.Count; ++i) {
3805 if (genArgs [i].Name == type.Name) {
3806 buf.Append ('`').Append (i);
3811 if (genDeclMethod != null) {
3812 GenericParameterCollection genArgs = null;
3813 if (genDeclMethod.IsGenericMethod ()) {
3814 genArgs = genDeclMethod.GenericParameters;
3815 for (int i = 0; i < genArgs.Count; ++i) {
3816 if (genArgs [i].Name == type.Name) {
3817 buf.Append ("``").Append (i);
3823 if (genDeclType == null && genDeclMethod == null) {
3824 // Probably from within an explicitly implemented interface member,
3825 // where CSC uses parameter names instead of indices (why?), e.g.
3826 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
3827 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
3828 buf.Append (type.Name);
3830 if (buf.Length == l) {
3831 throw new Exception (string.Format (
3832 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
3833 type.Name, genDeclType, genDeclMethod));
3837 base.AppendTypeName (buf, type);
3839 int numArgs = type.GenericParameters.Count;
3840 if (type.DeclaringType != null)
3841 numArgs -= type.GenericParameters.Count;
3843 buf.Append ('`').Append (numArgs);
3850 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3853 base.AppendGenericType (buf, type);
3855 AppendType (buf, type);
3859 private StringBuilder AppendType (StringBuilder buf, TypeReference type)
3861 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
3862 bool insertNested = false;
3863 int prevParamCount = 0;
3864 foreach (var decl in decls) {
3866 buf.Append (NestedTypeSeparator);
3867 insertNested = true;
3868 base.AppendTypeName (buf, decl);
3869 int argCount = DocUtils.GetGenericArgumentCount (decl);
3870 int numArgs = argCount - prevParamCount;
3871 prevParamCount = argCount;
3873 buf.Append ('`').Append (numArgs);
3878 public override string GetDeclaration (IMemberReference member)
3880 TypeReference r = member as TypeReference;
3882 return "T:" + GetTypeName (r);
3884 return base.GetDeclaration (member);
3887 protected override string GetConstructorName (MethodReference constructor)
3889 return GetMethodDefinitionName (constructor, "#ctor");
3892 protected override string GetMethodName (MethodReference method)
3895 MethodDefinition methodDef = method as MethodDefinition;
3896 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
3899 TypeReference iface;
3900 MethodReference ifaceMethod;
3901 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
3902 AddTypeCount = false;
3903 name = GetTypeName (iface) + "." + ifaceMethod.Name;
3904 AddTypeCount = true;
3906 return GetMethodDefinitionName (method, name);
3909 private string GetMethodDefinitionName (MethodReference method, string name)
3911 StringBuilder buf = new StringBuilder ();
3912 buf.Append (GetTypeName (method.DeclaringType));
3914 buf.Append (name.Replace (".", "#"));
3915 if (method.IsGenericMethod ()) {
3916 GenericParameterCollection genArgs = method.GenericParameters;
3917 if (genArgs.Count > 0)
3918 buf.Append ("``").Append (genArgs.Count);
3920 ParameterDefinitionCollection parameters = method.Parameters;
3922 genDeclType = method.DeclaringType;
3923 genDeclMethod = method;
3924 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
3928 genDeclMethod = null;
3930 return buf.ToString ();
3933 private StringBuilder AppendParameters (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinitionCollection parameters)
3935 if (parameters.Count == 0)
3940 AppendParameter (buf, genArgs, parameters [0]);
3941 for (int i = 1; i < parameters.Count; ++i) {
3943 AppendParameter (buf, genArgs, parameters [i]);
3946 return buf.Append (')');
3949 private StringBuilder AppendParameter (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinition parameter)
3951 AddTypeCount = false;
3952 buf.Append (GetTypeName (parameter.ParameterType));
3953 AddTypeCount = true;
3957 protected override string GetPropertyName (PropertyReference property)
3961 PropertyDefinition propertyDef = property as PropertyDefinition;
3962 MethodDefinition method = null;
3963 if (propertyDef != null)
3964 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
3965 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
3966 name = property.Name;
3968 TypeReference iface;
3969 MethodReference ifaceMethod;
3970 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3971 AddTypeCount = false;
3972 name = string.Join ("#", new string[]{
3973 GetTypeName (iface).Replace (".", "#"),
3974 DocUtils.GetMember (property.Name)
3976 AddTypeCount = true;
3979 StringBuilder buf = new StringBuilder ();
3980 buf.Append (GetName (property.DeclaringType));
3983 ParameterDefinitionCollection parameters = property.Parameters;
3984 if (parameters.Count > 0) {
3985 genDeclType = property.DeclaringType;
3987 GenericParameterCollection genArgs = property.DeclaringType.GenericParameters;
3988 AppendParameter (buf, genArgs, parameters [0]);
3989 for (int i = 1; i < parameters.Count; ++i) {
3991 AppendParameter (buf, genArgs, parameters [i]);
3996 return buf.ToString ();
3999 protected override string GetFieldName (FieldReference field)
4001 return string.Format ("{0}.{1}",
4002 GetName (field.DeclaringType), field.Name);
4005 protected override string GetEventName (EventReference e)
4007 return string.Format ("{0}.{1}",
4008 GetName (e.DeclaringType), e.Name);
4011 protected override string GetTypeDeclaration (TypeDefinition type)
4013 string name = GetName (type);
4019 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4021 string name = GetName (constructor);
4027 protected override string GetMethodDeclaration (MethodDefinition method)
4029 string name = GetName (method);
4032 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4033 genDeclType = method.DeclaringType;
4034 genDeclMethod = method;
4035 name += "~" + GetName (method.ReturnType.ReturnType);
4037 genDeclMethod = null;
4042 protected override string GetPropertyDeclaration (PropertyDefinition property)
4044 string name = GetName (property);
4050 protected override string GetFieldDeclaration (FieldDefinition field)
4052 string name = GetName (field);
4058 protected override string GetEventDeclaration (EventDefinition e)
4060 string name = GetName (e);
4067 class FileNameMemberFormatter : SlashDocMemberFormatter {
4068 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4073 protected override char NestedTypeSeparator {