1 // Updater program for syncing Mono's ECMA-style documentation files
3 // By Joshua Tauberer <tauberer@for.net>
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Diagnostics;
9 using System.Globalization;
14 using System.Xml.XPath;
19 using MyXmlNodeList = System.Collections.Generic.List<System.Xml.XmlNode>;
20 using StringList = System.Collections.Generic.List<string>;
21 using StringToStringMap = System.Collections.Generic.Dictionary<string, string>;
22 using StringToXmlNodeMap = System.Collections.Generic.Dictionary<string, System.Xml.XmlNode>;
24 namespace Mono.Documentation {
26 class MDocUpdater : MDocCommand
29 List<AssemblyDefinition> assemblies;
30 readonly DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver();
34 bool no_assembly_versions;
35 ExceptionLocations? exceptions;
37 int additions = 0, deletions = 0;
39 static XmlDocument slashdocs;
44 static readonly MemberFormatter csharpFullFormatter = new CSharpFullMemberFormatter ();
45 static readonly MemberFormatter csharpFormatter = new CSharpMemberFormatter ();
46 static readonly MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
47 static readonly MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
48 static readonly MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
50 MyXmlNodeList extensionMethods = new MyXmlNodeList ();
52 public override void Run (IEnumerable<string> args)
54 show_exceptions = DebugOutput;
56 var types = new List<string> ();
57 var p = new OptionSet () {
59 "Delete removed members from the XML files.",
60 v => delete = v != null },
62 "Document potential exceptions that members can generate. {SOURCES} " +
63 "is a comma-separated list of:\n" +
64 " asm Method calls in same assembly\n" +
65 " depasm Method calls in dependent assemblies\n" +
66 " all Record all possible exceptions\n" +
67 "If nothing is specified, then only exceptions from the member will " +
69 v => exceptions = ParseExceptionLocations (v) },
71 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
74 case "no-assembly-versions":
75 no_assembly_versions = true;
78 throw new Exception ("Unsupported flag `" + v + "'.");
81 { "fno-assembly-versions",
82 "Do not generate //AssemblyVersion elements.",
83 v => no_assembly_versions = v != null },
85 "Import documentation from {FILE}.",
88 "Check for assembly references in {DIRECTORY}.",
89 v => assemblyResolver.AddSearchDirectory (v) },
91 "Root {DIRECTORY} to generate/update documentation.",
94 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
95 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
96 v => assemblyResolver.AddSearchDirectory (Path.GetDirectoryName (v)) },
98 "Manually specify the assembly {VERSION} that new members were added in.",
101 "Only update documentation for {TYPE}.",
102 v => types.Add (v) },
104 var assemblies = Parse (p, args, "update",
105 "[OPTIONS]+ ASSEMBLIES",
106 "Create or update documentation from ASSEMBLIES.");
107 if (assemblies == null)
109 if (assemblies.Count == 0)
110 Error ("No assemblies specified.");
112 foreach (var dir in assemblies
113 .Where (a => a.Contains (Path.DirectorySeparatorChar))
114 .Select (a => Path.GetDirectoryName (a)))
115 assemblyResolver.AddSearchDirectory (dir);
117 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
120 throw new InvalidOperationException("The --out option is required.");
122 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
124 if (import != null && ecmadocs == null && slashdocs == null) {
126 XmlReader r = new XmlTextReader (import);
128 while (r.NodeType != XmlNodeType.Element) {
130 Error ("Unable to read XML file: {0}.", import);
132 if (r.LocalName == "doc") {
133 slashdocs = new XmlDocument();
134 slashdocs.Load (import);
136 else if (r.LocalName == "Libraries") {
137 ecmadocs = new XmlTextReader (import);
140 Error ("Unsupported XML format within {0}.", import);
143 } catch (Exception e) {
144 Environment.ExitCode = 1;
145 Error ("Could not load XML file: {0}.", e.Message);
149 // PERFORM THE UPDATES
152 DoUpdateTypes (srcPath, types, srcPath);
154 else if (opts.@namespace != null)
155 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
156 Path.Combine (dest_dir, opts.@namespace));
159 DoUpdateAssemblies (srcPath, srcPath);
161 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
164 static ExceptionLocations ParseExceptionLocations (string s)
166 ExceptionLocations loc = ExceptionLocations.Member;
169 foreach (var type in s.Split (',')) {
171 case "added": loc |= ExceptionLocations.AddedMembers; break;
172 case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
173 case "asm": loc |= ExceptionLocations.Assembly; break;
174 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
175 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
181 private void Warning (string format, params object[] args)
183 Message (TraceLevel.Warning, "mdoc: " + format, args);
186 private AssemblyDefinition LoadAssembly (string name)
188 AssemblyDefinition assembly = null;
190 assembly = AssemblyFactory.GetAssembly (name);
191 } catch (System.IO.FileNotFoundException) { }
193 if (assembly == null)
194 throw new InvalidOperationException("Assembly " + name + " not found.");
196 assembly.Resolver = assemblyResolver;
200 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
201 OrderTypeAttributes (element);
202 XmlTextWriter writer = new XmlTextWriter(output);
203 writer.Formatting = Formatting.Indented;
204 writer.Indentation = 2;
205 writer.IndentChar = ' ';
206 element.WriteTo(writer);
210 private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
212 if (!File.Exists (filename)) {
213 using (var writer = OpenWrite (filename, mode))
218 string tmpFile = filename + ".tmp";
222 using (var writer = OpenWrite (tmpFile, mode))
225 using (var a = File.OpenRead (filename))
226 using (var b = File.OpenRead (tmpFile)) {
227 if (a.Length == b.Length)
228 move = !FileContentsIdentical (a, b);
232 File.Delete (filename);
233 File.Move (tmpFile, filename);
237 if (!move && File.Exists (tmpFile))
238 File.Delete (tmpFile);
242 static bool FileContentsIdentical (Stream a, Stream b)
244 byte[] ba = new byte[4096];
245 byte[] bb = new byte[4096];
248 while ((ra = a.Read (ba, 0, ba.Length)) > 0 &&
249 (rb = b.Read (bb, 0, bb.Length)) > 0) {
252 for (int i = 0; i < ra; ++i) {
253 if (ba [i] != bb [i])
260 private static void OrderTypeAttributes (XmlElement e)
262 foreach (XmlElement type in e.SelectNodes ("//Type")) {
263 OrderTypeAttributes (type.Attributes);
267 static readonly string[] TypeAttributeOrder = {
268 "Name", "FullName", "FullNameSP", "Maintainer"
271 private static void OrderTypeAttributes (XmlAttributeCollection c)
273 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
274 for (int i = 0; i < c.Count; ++i) {
275 XmlAttribute a = c [i];
276 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
277 if (a.Name == TypeAttributeOrder [j]) {
283 for (int i = attrs.Length-1; i >= 0; --i) {
284 XmlAttribute n = attrs [i];
287 XmlAttribute r = null;
288 for (int j = i+1; j < attrs.Length; ++j) {
289 if (attrs [j] != null) {
297 c.InsertBefore (n, r);
301 private XmlDocument CreateIndexStub()
303 XmlDocument index = new XmlDocument();
305 XmlElement index_root = index.CreateElement("Overview");
306 index.AppendChild(index_root);
308 if (assemblies.Count == 0)
309 throw new Exception ("No assembly");
311 XmlElement index_assemblies = index.CreateElement("Assemblies");
312 index_root.AppendChild(index_assemblies);
314 XmlElement index_remarks = index.CreateElement("Remarks");
315 index_remarks.InnerText = "To be added.";
316 index_root.AppendChild(index_remarks);
318 XmlElement index_copyright = index.CreateElement("Copyright");
319 index_copyright.InnerText = "To be added.";
320 index_root.AppendChild(index_copyright);
322 XmlElement index_types = index.CreateElement("Types");
323 index_root.AppendChild(index_types);
328 private static void WriteNamespaceStub(string ns, string outdir) {
329 XmlDocument index = new XmlDocument();
331 XmlElement index_root = index.CreateElement("Namespace");
332 index.AppendChild(index_root);
334 index_root.SetAttribute("Name", ns);
336 XmlElement index_docs = index.CreateElement("Docs");
337 index_root.AppendChild(index_docs);
339 XmlElement index_summary = index.CreateElement("summary");
340 index_summary.InnerText = "To be added.";
341 index_docs.AppendChild(index_summary);
343 XmlElement index_remarks = index.CreateElement("remarks");
344 index_remarks.InnerText = "To be added.";
345 index_docs.AppendChild(index_remarks);
347 WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
348 writer => WriteXml (index.DocumentElement, writer));
351 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
353 var found = new HashSet<string> ();
354 foreach (AssemblyDefinition assembly in assemblies) {
355 foreach (DocsTypeInfo docsTypeInfo in GetTypes (assembly, typenames)) {
356 string relpath = DoUpdateType (docsTypeInfo.Type, basepath, dest, docsTypeInfo.EcmaDocs);
358 found.Add (docsTypeInfo.Type.FullName);
361 var notFound = from n in typenames where !found.Contains (n) select n;
363 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
366 public string DoUpdateType (TypeDefinition type, string basepath, string dest, XmlReader ecmaDocsType)
368 if (type.Namespace == null)
369 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
371 if (!IsPublic (type))
374 // Must get the A+B form of the type name.
375 string typename = GetTypeFileName(type);
377 string reltypefile = DocUtils.PathCombine (DocUtils.GetNamespace (type), typename + ".xml");
378 string typefile = Path.Combine (basepath, reltypefile);
379 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
381 string output = null;
384 } else if (dest == "-") {
387 output = Path.Combine (dest, reltypefile);
392 XmlDocument basefile = new XmlDocument();
394 basefile.Load(typefile);
395 } catch (Exception e) {
396 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
399 DoUpdateType2("Updating", basefile, type, output, false, ecmaDocsType);
402 XmlElement td = StubType(type, output, ecmaDocsType);
406 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
409 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
415 public void DoUpdateNS (string ns, string nspath, string outpath)
417 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
418 AssemblyDefinition assembly = assemblies [0];
420 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
421 XmlDocument basefile = new XmlDocument();
422 string typefile = Path.Combine(nspath, file.Name);
424 basefile.Load(typefile);
425 } catch (Exception e) {
426 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
430 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
431 TypeDefinition type = assembly.GetType(typename);
433 Warning ("Type no longer in assembly: " + typename);
437 seenTypes[type] = seenTypes;
438 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false, null);
441 // Stub types not in the directory
442 foreach (DocsTypeInfo docsTypeInfo in GetTypes (assembly, null)) {
443 TypeDefinition type = docsTypeInfo.Type;
444 if (type.Namespace != ns || seenTypes.ContainsKey(type))
447 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"), docsTypeInfo.EcmaDocs);
448 if (td == null) continue;
452 private static string GetTypeFileName (TypeReference type)
454 return filenameFormatter.GetName (type);
457 public static string GetTypeFileName (string typename)
459 StringBuilder filename = new StringBuilder (typename.Length);
463 for (int i = 0; i < typename.Length; ++i) {
464 char c = typename [i];
473 filename.Append ('`').Append ((numArgs+1).ToString());
488 return filename.ToString ();
491 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
493 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
494 index_assembly.SetAttribute ("Name", assembly.Name.Name);
495 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
496 MakeAttributes (index_assembly, assembly.CustomAttributes, 0);
497 parent.AppendChild(index_assembly);
500 private void DoUpdateAssemblies (string source, string dest)
502 string indexfile = dest + "/index.xml";
504 if (System.IO.File.Exists(indexfile)) {
505 index = new XmlDocument();
506 index.Load(indexfile);
509 ClearElement(index.DocumentElement, "Assembly");
510 ClearElement(index.DocumentElement, "Attributes");
512 index = CreateIndexStub();
515 string defaultTitle = "Untitled";
516 if (assemblies.Count == 1)
517 defaultTitle = assemblies[0].Name.Name;
518 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
520 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
521 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
522 index_assemblies.RemoveAll ();
525 HashSet<string> goodfiles = new HashSet<string> ();
527 foreach (AssemblyDefinition assm in assemblies) {
528 AddIndexAssembly (assm, index_assemblies);
529 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
532 SortIndexEntries (index_types);
534 CleanupFiles (dest, goodfiles);
535 CleanupIndexTypes (index_types, goodfiles);
536 CleanupExtensions (index_types);
538 WriteFile (indexfile, FileMode.Create,
539 writer => WriteXml(index.DocumentElement, writer));
542 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
544 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
546 foreach (DocsTypeInfo docTypeInfo in GetTypes (assembly, null)) {
547 TypeDefinition type = docTypeInfo.Type;
548 string typename = GetTypeFileName(type);
549 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0)
552 string reltypepath = DoUpdateType (type, source, dest, docTypeInfo.EcmaDocs);
553 if (reltypepath == null)
556 // Add namespace and type nodes into the index file as needed
557 string ns = DocUtils.GetNamespace (type);
558 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode("Namespace[@Name='" + ns + "']");
559 if (nsnode == null) {
560 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
561 nsnode.SetAttribute ("Name", ns);
562 index_types.AppendChild(nsnode);
564 string doc_typename = GetDocTypeName (type);
565 XmlElement typenode = (XmlElement)nsnode.SelectSingleNode("Type[@Name='" + typename + "']");
566 if (typenode == null) {
567 typenode = index_types.OwnerDocument.CreateElement("Type");
568 typenode.SetAttribute("Name", typename);
569 nsnode.AppendChild(typenode);
571 if (typename != doc_typename)
572 typenode.SetAttribute("DisplayName", doc_typename);
574 typenode.RemoveAttribute("DisplayName");
575 typenode.SetAttribute ("Kind", GetTypeKind (type));
577 // Ensure the namespace index file exists
578 string onsdoc = DocUtils.PathCombine (dest, type.Namespace + ".xml");
579 string nsdoc = DocUtils.PathCombine (dest, "ns-" + type.Namespace + ".xml");
580 if (File.Exists (onsdoc)) {
581 File.Move (onsdoc, nsdoc);
584 if (!File.Exists (nsdoc)) {
585 Console.WriteLine("New Namespace File: " + type.Namespace);
586 WriteNamespaceStub(type.Namespace, dest);
589 goodfiles.Add (reltypepath);
594 public TypeDefinition Type;
595 public XmlReader EcmaDocs;
597 public DocsTypeInfo (TypeDefinition type, XmlReader docs)
600 this.EcmaDocs = docs;
604 IEnumerable<Mono.Documentation.MDocUpdater.DocsTypeInfo> GetTypes (AssemblyDefinition assembly, List<string> forTypes)
606 HashSet<string> seen = null;
607 if (forTypes != null)
609 if (ecmadocs != null) {
610 seen = new HashSet<string> ();
612 while (ecmadocs.Read ()) {
613 switch (ecmadocs.Name) {
616 typeDepth = ecmadocs.Depth;
617 if (ecmadocs.NodeType != XmlNodeType.Element)
619 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
621 string typename = ecmadocs.GetAttribute ("FullName");
622 string typename2 = GetTypeFileName (typename);
623 if (forTypes != null &&
624 forTypes.BinarySearch (typename) < 0 &&
625 typename != typename2 &&
626 forTypes.BinarySearch (typename2) < 0)
629 if ((t = assembly.GetType (typename)) == null &&
630 (t = assembly.GetType (typename2)) == null)
633 if (typename != typename2)
634 seen.Add (typename2);
635 Console.WriteLine (" Import: {0}", t.FullName);
636 yield return new DocsTypeInfo (t, ecmadocs);
644 foreach (TypeDefinition type in assembly.GetTypes()) {
645 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
647 if (seen != null && seen.Contains (type.FullName))
649 yield return new DocsTypeInfo (type, null);
650 foreach (TypeDefinition nested in type.NestedTypes)
651 yield return new DocsTypeInfo (nested, null);
655 private static void SortIndexEntries (XmlElement indexTypes)
657 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
658 XmlNodeComparer c = new AttributeNameComparer ();
659 SortXmlNodes (indexTypes, namespaces, c);
661 for (int i = 0; i < namespaces.Count; ++i)
662 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
665 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
667 MyXmlNodeList l = new MyXmlNodeList (children.Count);
668 for (int i = 0; i < children.Count; ++i)
669 l.Add (children [i]);
671 for (int i = l.Count - 1; i > 0; --i) {
672 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
676 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
678 public abstract int Compare (XmlNode x, XmlNode y);
680 public int Compare (object x, object y)
682 return Compare ((XmlNode) x, (XmlNode) y);
686 class AttributeNameComparer : XmlNodeComparer {
689 public AttributeNameComparer ()
694 public AttributeNameComparer (string attribute)
696 this.attribute = attribute;
699 public override int Compare (XmlNode x, XmlNode y)
701 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
705 class VersionComparer : XmlNodeComparer {
706 public override int Compare (XmlNode x, XmlNode y)
708 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
709 string a = GetVersion (x.InnerText);
710 string b = GetVersion (y.InnerText);
711 return new Version (a).CompareTo (new Version (b));
714 static string GetVersion (string v)
716 int n = v.IndexOf ("x");
719 return v.Substring (0, n-1);
723 private static string GetTypeKind (TypeDefinition type)
726 return "Enumeration";
727 if (type.IsValueType)
729 if (type.IsInterface)
731 if (DocUtils.IsDelegate (type))
733 if (type.IsClass || type.FullName == "System.Enum") // FIXME
735 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
738 private static bool IsPublic (TypeDefinition type)
740 TypeDefinition decl = type;
741 while (decl != null) {
742 if (!(decl.IsPublic || decl.IsNestedPublic)) {
745 decl = (TypeDefinition) decl.DeclaringType;
750 private void CleanupFiles (string dest, HashSet<string> goodfiles)
752 // Look for files that no longer correspond to types
753 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
754 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
755 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
756 if (!goodfiles.Contains (relTypeFile)) {
757 XmlDocument doc = new XmlDocument ();
758 doc.Load (typefile.FullName);
759 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
760 if (UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
761 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
762 WriteXml(doc.DocumentElement, writer);
763 goodfiles.Add (relTypeFile);
766 string newname = typefile.FullName + ".remove";
767 try { System.IO.File.Delete(newname); } catch (Exception) { }
768 try { typefile.MoveTo(newname); } catch (Exception) { }
769 Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
775 private static TextWriter OpenWrite (string path, FileMode mode)
777 return new StreamWriter (
778 new FileStream (path, mode),
779 new UTF8Encoding (false)
783 private string[] GetAssemblyVersions ()
785 return (from a in assemblies select GetAssemblyVersion (a)).ToArray ();
788 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
790 // Look for type nodes that no longer correspond to types
791 MyXmlNodeList remove = new MyXmlNodeList ();
792 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
793 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
794 if (!goodfiles.Contains (fulltypename)) {
795 remove.Add (typenode);
798 foreach (XmlNode n in remove)
799 n.ParentNode.RemoveChild (n);
802 private void CleanupExtensions (XmlElement index_types)
804 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
805 if (extensionMethods.Count == 0) {
808 index_types.RemoveChild (e);
812 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
813 index_types.SelectSingleNode ("/Overview").AppendChild (e);
817 extensionMethods.Sort (DefaultExtensionMethodComparer);
818 foreach (XmlNode m in extensionMethods) {
819 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
823 class ExtensionMethodComparer : XmlNodeComparer {
824 public override int Compare (XmlNode x, XmlNode y)
826 XmlNode xLink = x.SelectSingleNode ("Member/Link");
827 XmlNode yLink = y.SelectSingleNode ("Member/Link");
829 int n = xLink.Attributes ["Type"].Value.CompareTo (
830 yLink.Attributes ["Type"].Value);
833 n = xLink.Attributes ["Member"].Value.CompareTo (
834 yLink.Attributes ["Member"].Value);
835 if (n == 0 && !object.ReferenceEquals (x, y))
836 throw new InvalidOperationException ("Duplicate extension method found!");
841 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
843 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince, XmlReader ecmaDocsType)
845 Console.WriteLine(message + ": " + type.FullName);
847 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
849 // Update type metadata
850 UpdateType(basefile.DocumentElement, type, ecmaDocsType);
852 if (ecmaDocsType != null) {
853 while (ecmaDocsType.Name != "Members" && ecmaDocsType.Read ()) {
856 if (ecmaDocsType.IsEmptyElement)
860 // Update existing members. Delete member nodes that no longer should be there,
861 // and remember what members are already documented so we don't add them again.
863 MyXmlNodeList todelete = new MyXmlNodeList ();
864 foreach (DocsNodeInfo info in GetDocumentationMembers (basefile, type, ecmaDocsType)) {
865 XmlElement oldmember = info.Node;
866 IMemberReference oldmember2 = info.Member;
867 string sig = oldmember2 != null ? MakeMemberSignature(oldmember2) : null;
869 // Interface implementations and overrides are deleted from the docs
870 // unless the overrides option is given.
871 if (oldmember2 != null && sig == null)
874 // Deleted (or signature changed)
875 if (oldmember2 == null) {
876 if (UpdateAssemblyVersions (oldmember, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
878 DeleteMember ("Member Removed", output, oldmember, todelete);
883 if (seenmembers.ContainsKey (sig)) {
884 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
885 // ignore, already seen
887 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
888 DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
890 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
894 // Update signature information
897 seenmembers.Add (sig, oldmember);
899 foreach (XmlElement oldmember in todelete)
900 oldmember.ParentNode.RemoveChild (oldmember);
903 if (!DocUtils.IsDelegate (type)) {
904 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
905 foreach (IMemberReference m in type.GetMembers()) {
906 if (m is TypeDefinition) continue;
908 string sig = MakeMemberSignature(m);
909 if (sig == null) continue;
910 if (seenmembers.ContainsKey(sig)) continue;
912 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
913 if (mm == null) continue;
914 members.AppendChild( mm );
916 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
921 // Import code snippets from files
922 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
923 if (!(code is XmlElement)) continue;
924 string file = ((XmlElement)code).GetAttribute("src");
925 string lang = ((XmlElement)code).GetAttribute("lang");
927 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
929 code.InnerText = src;
933 if (insertSince && since != null) {
934 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
935 docs.AppendChild (CreateSinceNode (basefile));
939 XmlElement d = basefile.DocumentElement ["Docs"];
940 XmlElement m = basefile.DocumentElement ["Members"];
941 if (d != null && m != null)
942 basefile.DocumentElement.InsertBefore (
943 basefile.DocumentElement.RemoveChild (d), m);
948 WriteXml(basefile.DocumentElement, Console.Out);
950 FileInfo file = new FileInfo (output);
951 if (!file.Directory.Exists) {
952 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
953 file.Directory.Create ();
955 WriteFile (output, FileMode.Create,
956 writer => WriteXml(basefile.DocumentElement, writer));
960 private string GetCodeSource (string lang, string file)
963 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
964 // Grab the specified region
965 string region = "#region " + file.Substring (anchorStart + 4);
966 file = file.Substring (0, anchorStart + 3);
968 using (StreamReader reader = new StreamReader (file)) {
970 StringBuilder src = new StringBuilder ();
972 while ((line = reader.ReadLine ()) != null) {
973 if (line.Trim() == region) {
974 indent = line.IndexOf (region);
977 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
982 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
985 return src.ToString ();
987 } catch (Exception e) {
988 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
989 file, region, show_exceptions ? e.ToString () : e.Message);
994 using (StreamReader reader = new StreamReader (file))
995 return reader.ReadToEnd ();
996 } catch (Exception e) {
997 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
1002 private IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type, XmlReader ecmaDocsMembers)
1004 if (ecmaDocsMembers != null) {
1005 int membersDepth = ecmaDocsMembers.Depth;
1007 while (go && ecmaDocsMembers.Read ()) {
1008 switch (ecmaDocsMembers.Name) {
1010 if (membersDepth != ecmaDocsMembers.Depth - 1 || ecmaDocsMembers.NodeType != XmlNodeType.Element)
1012 DocumentationMember dm = new DocumentationMember (ecmaDocsMembers);
1013 string xp = GetXPathForMember (dm);
1014 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
1016 if (oldmember == null) {
1017 m = GetMember (type, dm);
1019 Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
1020 type.FullName, dm.MemberSignatures ["C#"]);
1021 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
1024 // oldmember lookup may have failed due to type parameter renames.
1026 oldmember = (XmlElement) basefile.SelectSingleNode (GetXPathForMember (m));
1027 if (oldmember == null) {
1028 XmlElement members = WriteElement(basefile.DocumentElement, "Members");
1029 oldmember = basefile.CreateElement ("Member");
1030 oldmember.SetAttribute ("MemberName", dm.MemberName);
1031 members.AppendChild (oldmember);
1032 foreach (string key in Sort (dm.MemberSignatures.Keys)) {
1033 XmlElement ms = basefile.CreateElement ("MemberSignature");
1034 ms.SetAttribute ("Language", key);
1035 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
1036 oldmember.AppendChild (ms);
1038 oldmember.SetAttribute ("__monodocer-seen__", "true");
1039 Console.WriteLine ("Member Added: {0}", MakeMemberSignature (m));
1044 m = GetMember (type, new DocumentationMember (oldmember));
1046 Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
1047 type.FullName, dm.MemberSignatures ["C#"]);
1050 oldmember.SetAttribute ("__monodocer-seen__", "true");
1052 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
1053 if (ecmaDocsMembers.Name != "Docs")
1054 throw new InvalidOperationException ("Found " + ecmaDocsMembers.Name + "; expected <Docs/>!");
1055 node.EcmaDocs = ecmaDocsMembers;
1060 if (membersDepth == ecmaDocsMembers.Depth && ecmaDocsMembers.NodeType == XmlNodeType.EndElement) {
1067 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
1068 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
1069 oldmember.RemoveAttribute ("__monodocer-seen__");
1072 IMemberReference m = GetMember (type, new DocumentationMember (oldmember));
1074 yield return new DocsNodeInfo (oldmember);
1077 yield return new DocsNodeInfo (oldmember, m);
1082 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
1084 string format = output != null
1085 ? "{0}: File='{1}'; Signature='{4}'"
1086 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1090 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1091 member.Attributes ["MemberName"].Value,
1092 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
1093 if (!delete && MemberDocsHaveUserContent (member)) {
1094 Warning ("Member deletions must be enabled with the --delete option.");
1096 todelete.Add (member);
1101 class MemberComparer : XmlNodeComparer {
1102 public override int Compare (XmlNode x, XmlNode y)
1105 string xMemberName = x.Attributes ["MemberName"].Value;
1106 string yMemberName = y.Attributes ["MemberName"].Value;
1108 // generic methods *end* with '>'
1109 // it's possible for explicitly implemented generic interfaces to
1110 // contain <...> without being a generic method
1111 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1112 (r = xMemberName.CompareTo (yMemberName)) != 0)
1116 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1117 xMemberName = xMemberName.Substring (0, lt);
1118 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1119 yMemberName = yMemberName.Substring (0, lt);
1120 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1123 // if @MemberName matches, then it's either two different types of
1124 // members sharing the same name, e.g. field & property, or it's an
1125 // overloaded method.
1126 // for different type, sort based on MemberType value.
1127 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1128 y.SelectSingleNode ("MemberType").InnerText);
1132 // same type -- must be an overloaded method. Sort based on type
1133 // parameter count, then parameter count, then by the parameter
1135 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1136 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1137 if (xTypeParams.Count != yTypeParams.Count)
1138 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1139 for (int i = 0; i < xTypeParams.Count; ++i) {
1140 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1141 yTypeParams [i].Attributes ["Name"].Value);
1146 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1147 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1148 if (xParams.Count != yParams.Count)
1149 return xParams.Count <= yParams.Count ? -1 : 1;
1150 for (int i = 0; i < xParams.Count; ++i) {
1151 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1152 yParams [i].Attributes ["Type"].Value);
1156 // all parameters match, but return value might not match if it was
1157 // changed between one version and another.
1158 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1159 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1160 if (xReturn != null && yReturn != null) {
1161 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1170 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1172 private static void SortTypeMembers (XmlNode members)
1174 if (members == null)
1176 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1179 private static bool MemberDocsHaveUserContent (XmlNode e)
1181 e = (XmlElement)e.SelectSingleNode("Docs");
1182 if (e == null) return false;
1183 foreach (XmlElement d in e.SelectNodes("*"))
1184 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1189 // UPDATE HELPER FUNCTIONS
1191 private static IMemberReference GetMember (TypeDefinition type, DocumentationMember member)
1193 string membertype = member.MemberType;
1195 string returntype = member.ReturnType;
1197 string docName = member.MemberName;
1198 string[] docTypeParams = GetTypeParameters (docName);
1200 // Loop through all members in this type with the same name
1201 foreach (IMemberReference mi in GetReflectionMembers (type, docName)) {
1202 if (mi is TypeDefinition) continue;
1203 if (GetMemberType(mi) != membertype) continue;
1205 string sig = MakeMemberSignature(mi);
1206 if (sig == null) continue; // not publicly visible
1208 ParameterDefinitionCollection pis = null;
1209 string[] typeParams = null;
1210 if (mi is MethodDefinition) {
1211 MethodDefinition mb = (MethodDefinition) mi;
1212 pis = mb.Parameters;
1213 if (docTypeParams != null && mb.IsGenericMethod ()) {
1214 GenericParameterCollection args = mb.GenericParameters;
1215 if (args.Count == docTypeParams.Length) {
1216 typeParams = args.Cast<GenericParameter> ().Select (p => p.Name).ToArray ();
1220 else if (mi is PropertyDefinition)
1221 pis = ((PropertyDefinition)mi).Parameters;
1223 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
1224 int pcount = pis == null ? 0 : pis.Count;
1225 if (mcount != pcount)
1228 MethodDefinition mDef = mi as MethodDefinition;
1229 if (mDef != null && !mDef.IsConstructor) {
1230 // Casting operators can overload based on return type.
1231 if (returntype != GetReplacedString (
1232 GetDocTypeFullName (((MethodDefinition)mi).ReturnType.ReturnType),
1233 typeParams, docTypeParams)) {
1241 for (int i = 0; i < pis.Count; i++) {
1242 string paramType = GetReplacedString (
1243 GetDocParameterType (pis [i].ParameterType),
1244 typeParams, docTypeParams);
1245 if (paramType != (string) member.Parameters [i]) {
1250 if (!good) continue;
1258 private static IEnumerable<IMemberReference> GetReflectionMembers (TypeDefinition type, string docName)
1260 // need to worry about 4 forms of //@MemberName values:
1261 // 1. "Normal" (non-generic) member names: GetEnumerator
1263 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
1264 // - try as-is, and try type.member (due to "kludge" for property
1266 // 3. "Normal" Generic member names: Sort<T> (CSC)
1267 // - need to remove generic parameters --> "Sort"
1268 // 4. Explicitly-implemented interface members for generic interfaces:
1269 // -- System.Collections.Generic.IEnumerable<T>.Current
1270 // - Try as-is, and try type.member, *keeping* the generic parameters.
1271 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
1272 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
1273 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
1274 // this as (1) or (2).
1275 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
1277 foreach (IMemberReference mi in type.GetMembers (docName))
1279 if (CountChars (docName, '.') > 0)
1280 // might be a property; try only type.member instead of
1281 // namespace.type.member.
1282 foreach (IMemberReference mi in
1283 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
1290 int startLt, startType, startMethod;
1291 startLt = startType = startMethod = -1;
1292 for (int i = 0; i < docName.Length; ++i) {
1293 switch (docName [i]) {
1302 if (numLt == 0 && (i + 1) < docName.Length)
1303 // there's another character in docName, so this <...> sequence is
1304 // probably part of a generic type -- case 4.
1308 startType = startMethod;
1314 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
1316 foreach (IMemberReference mi in type.GetMembers (refName))
1320 foreach (IMemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
1323 // If we _still_ haven't found it, we've hit another generic naming issue:
1324 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
1325 // explicitly-implemented METHOD names (not properties), e.g.
1326 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
1327 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
1328 // which the XML docs will contain.
1330 // Alas, we can't derive the Mono name from docName, so we need to iterate
1331 // over all member names, convert them into CSC format, and compare... :-(
1334 foreach (IMemberReference mi in type.GetMembers ()) {
1335 if (GetMemberName (mi) == docName)
1340 static string[] GetTypeParameters (string docName)
1342 if (docName [docName.Length-1] != '>')
1344 StringList types = new StringList ();
1345 int endToken = docName.Length-2;
1346 int i = docName.Length-2;
1348 if (docName [i] == ',' || docName [i] == '<') {
1349 types.Add (docName.Substring (i + 1, endToken - i));
1352 if (docName [i] == '<')
1357 return types.ToArray ();
1360 static string GetReplacedString (string typeName, string[] from, string[] to)
1364 for (int i = 0; i < from.Length; ++i)
1365 typeName = typeName.Replace (from [i], to [i]);
1369 // CREATE A STUB DOCUMENTATION FILE
1371 public XmlElement StubType (TypeDefinition type, string output, XmlReader ecmaDocsType)
1373 string typesig = MakeTypeSignature(type);
1374 if (typesig == null) return null; // not publicly visible
1376 XmlDocument doc = new XmlDocument();
1377 XmlElement root = doc.CreateElement("Type");
1378 doc.AppendChild (root);
1380 DoUpdateType2 ("New Type", doc, type, output, true, ecmaDocsType);
1385 private XmlElement CreateSinceNode (XmlDocument doc)
1387 XmlElement s = doc.CreateElement ("since");
1388 s.SetAttribute ("version", since);
1392 // STUBBING/UPDATING FUNCTIONS
1394 public void UpdateType (XmlElement root, TypeDefinition type, XmlReader ecmaDocsType)
1396 root.SetAttribute("Name", GetDocTypeName (type));
1397 root.SetAttribute("FullName", GetDocTypeFullName (type));
1399 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Language", "C#");
1400 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Value", MakeTypeSignature(type));
1402 XmlElement ass = WriteElement(root, "AssemblyInfo");
1403 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1404 if (!no_assembly_versions) {
1405 UpdateAssemblyVersions (root, type, true);
1408 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1409 foreach (var version in versions)
1410 ass.RemoveChild (version);
1412 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1413 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1415 ClearElement(ass, "AssemblyCulture");
1417 // Why-oh-why do we put assembly attributes in each type file?
1418 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1419 // since they're outdated in current docs, and a waste of space.
1420 //MakeAttributes(ass, type.Assembly, true);
1421 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1422 if (assattrs != null)
1423 ass.RemoveChild(assattrs);
1425 NormalizeWhitespace(ass);
1427 if (type.IsGenericType ()) {
1428 MakeTypeParameters (root, type.GenericParameters);
1430 ClearElement(root, "TypeParameters");
1433 if (type.BaseType != null) {
1434 XmlElement basenode = WriteElement(root, "Base");
1436 string basetypename = GetDocTypeFullName (type.BaseType);
1437 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1438 WriteElementText(root, "Base/BaseTypeName", basetypename);
1440 // Document how this type instantiates the generic parameters of its base type
1441 TypeReference origBase = type.BaseType.GetOriginalType ();
1442 if (origBase.IsGenericType ()) {
1443 ClearElement(basenode, "BaseTypeArguments");
1444 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1445 GenericArgumentCollection baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1446 GenericParameterCollection baseGenParams = origBase.GenericParameters;
1447 if (baseGenArgs.Count != baseGenParams.Count)
1448 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1449 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1450 GenericParameter param = baseGenParams [i];
1451 TypeReference value = baseGenArgs [i];
1453 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1454 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1455 bta.AppendChild(arg);
1456 arg.SetAttribute ("TypeParamName", param.Name);
1457 arg.InnerText = GetDocTypeFullName (value);
1461 ClearElement(root, "Base");
1464 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1465 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1466 List<string> interface_names = userInterfaces
1467 .Select (iface => GetDocTypeFullName (iface))
1471 XmlElement interfaces = WriteElement(root, "Interfaces");
1472 interfaces.RemoveAll();
1473 foreach (string iname in interface_names) {
1474 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1475 interfaces.AppendChild(iface);
1476 WriteElementText(iface, "InterfaceName", iname);
1479 ClearElement(root, "Interfaces");
1482 MakeAttributes (root, type.CustomAttributes, 0);
1484 if (DocUtils.IsDelegate (type)) {
1485 MakeTypeParameters (root, type.GenericParameters);
1486 MakeParameters(root, type.GetMethod("Invoke").Parameters);
1487 MakeReturnValue(root, type.GetMethod("Invoke"));
1490 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1491 if (ecmaDocsType != null) {
1492 if (ecmaDocsType.Name != "Docs") {
1493 int depth = ecmaDocsType.Depth;
1494 while (ecmaDocsType.Read ()) {
1495 if (ecmaDocsType.Name == "Docs" && ecmaDocsType.Depth == depth + 1)
1499 if (!ecmaDocsType.IsStartElement ("Docs"))
1500 throw new InvalidOperationException ("Found " + ecmaDocsType.Name + "; expecting <Docs/>!");
1501 typeInfo.EcmaDocs = ecmaDocsType;
1503 MakeDocNode (typeInfo);
1505 if (!DocUtils.IsDelegate (type))
1506 WriteElement (root, "Members");
1508 NormalizeWhitespace(root);
1511 static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1513 List<T> l = new List<T> (list);
1518 private void UpdateMember (DocsNodeInfo info)
1520 XmlElement me = (XmlElement) info.Node;
1521 IMemberReference mi = info.Member;
1522 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Language", "C#");
1523 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Value", MakeMemberSignature(mi));
1525 WriteElementText(me, "MemberType", GetMemberType(mi));
1527 if (!no_assembly_versions) {
1528 UpdateAssemblyVersions (me, mi, true);
1531 ClearElement (me, "AssemblyInfo");
1534 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1536 MakeAttributes (me, p.CustomAttributes, 0);
1538 PropertyReference pr = mi as PropertyReference;
1540 PropertyDefinition pd = pr.Resolve ();
1541 if (pd.GetMethod != null)
1542 MakeAttributes (me, pd.GetMethod.CustomAttributes, AttributeFlags.KeepExistingAttributes, "get: ");
1543 if (pd.SetMethod != null)
1544 MakeAttributes (me, pd.SetMethod.CustomAttributes, AttributeFlags.KeepExistingAttributes, "set: ");
1546 EventReference er = mi as EventReference;
1548 EventDefinition ed = er.Resolve ();
1549 if (ed.AddMethod != null)
1550 MakeAttributes (me, ed.AddMethod.CustomAttributes, AttributeFlags.KeepExistingAttributes, "add: ");
1551 if (ed.RemoveMethod != null)
1552 MakeAttributes (me, ed.RemoveMethod.CustomAttributes, AttributeFlags.KeepExistingAttributes, "remove: ");
1555 MakeReturnValue(me, mi);
1556 if (mi is MethodReference) {
1557 MethodReference mb = (MethodReference) mi;
1558 if (mb.IsGenericMethod ())
1559 MakeTypeParameters (me, mb.GenericParameters);
1561 MakeParameters(me, mi);
1564 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1565 WriteElementText(me, "MemberValue", fieldValue);
1567 info.Node = WriteElement (me, "Docs");
1569 UpdateExtensionMethods (me, info);
1572 static readonly string[] ValidExtensionMembers = {
1581 static readonly string[] ValidExtensionDocMembers = {
1587 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1589 MethodDefinition me = info.Member as MethodDefinition;
1592 if (info.Parameters.Count < 1)
1594 if (!DocUtils.IsExtensionMethod (me))
1597 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1598 XmlNode member = e.CloneNode (true);
1599 em.AppendChild (member);
1600 RemoveExcept (member, ValidExtensionMembers);
1601 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1602 WriteElementText (member, "MemberType", "ExtensionMethod");
1603 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1604 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1605 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1606 member.AppendChild (link);
1607 AddTargets (em, info);
1609 extensionMethods.Add (em);
1612 private static void RemoveExcept (XmlNode node, string[] except)
1616 MyXmlNodeList remove = null;
1617 foreach (XmlNode n in node.ChildNodes) {
1618 if (Array.BinarySearch (except, n.Name) < 0) {
1620 remove = new MyXmlNodeList ();
1625 foreach (XmlNode n in remove)
1626 node.RemoveChild (n);
1629 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1631 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1632 member.PrependChild (targets);
1633 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1634 AppendElementAttributeText (targets, "Target", "Type",
1635 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1638 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1639 ConstraintCollection constraints = gp.Constraints;
1640 if (constraints.Count == 0)
1641 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1643 foreach (TypeReference c in constraints)
1644 AppendElementAttributeText(targets, "Target", "Type",
1645 slashdocFormatter.GetDeclaration (c));
1649 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1652 TypeDefinition type = field.DeclaringType.Resolve ();
1653 if (type != null && type.IsEnum) return false;
1655 if (type != null && type.IsGenericType ()) return false;
1656 if (!field.HasConstant)
1658 if (field.IsLiteral) {
1659 object val = field.Constant;
1660 if (val == null) value = "null";
1661 else if (val is Enum) value = val.ToString();
1662 else if (val is IFormattable) {
1663 value = ((IFormattable)val).ToString();
1665 value = "\"" + value + "\"";
1667 if (value != null && value != "")
1673 // XML HELPER FUNCTIONS
1675 private static XmlElement WriteElement(XmlNode parent, string element) {
1676 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1678 string[] path = element.Split('/');
1679 foreach (string p in path) {
1680 ret = (XmlElement)parent.SelectSingleNode(p);
1683 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1684 ename = ename.Substring(0, ename.IndexOf('['));
1685 ret = parent.OwnerDocument.CreateElement(ename);
1686 parent.AppendChild(ret);
1695 private static void WriteElementText(XmlNode parent, string element, string value) {
1696 XmlElement node = WriteElement(parent, element);
1697 node.InnerText = value;
1700 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1702 XmlElement n = parent.OwnerDocument.CreateElement (element);
1703 parent.AppendChild (n);
1704 n.InnerText = value;
1708 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1710 XmlElement n = parent.OwnerDocument.CreateElement (element);
1711 parent.AppendChild (n);
1712 n.SetAttribute (attribute, value);
1716 private static XmlNode CopyNode (XmlNode source, XmlNode dest)
1718 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1719 dest.AppendChild (copy);
1723 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1724 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1727 node = WriteElement(parent, element);
1728 node.InnerText = value;
1730 private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
1731 XmlElement node = WriteElement(parent, element);
1732 if (node.GetAttribute(attribute) == value) return;
1733 node.SetAttribute(attribute, value);
1735 private static void ClearElement(XmlElement parent, string name) {
1736 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1738 parent.RemoveChild(node);
1741 // DOCUMENTATION HELPER FUNCTIONS
1743 private void MakeDocNode (DocsNodeInfo info)
1745 List<GenericParameter> genericParams = info.GenericParameters;
1746 ParameterDefinitionCollection parameters = info.Parameters;
1747 TypeReference returntype = info.ReturnType;
1748 bool returnisreturn = info.ReturnIsReturn;
1749 XmlElement e = info.Node;
1750 bool addremarks = info.AddRemarks;
1752 WriteElementInitialText(e, "summary", "To be added.");
1754 if (parameters != null) {
1755 string[] values = new string [parameters.Count];
1756 for (int i = 0; i < values.Length; ++i)
1757 values [i] = parameters [i].Name;
1758 UpdateParameters (e, "param", values);
1761 if (genericParams != null) {
1762 string[] values = new string [genericParams.Count];
1763 for (int i = 0; i < values.Length; ++i)
1764 values [i] = genericParams [i].Name;
1765 UpdateParameters (e, "typeparam", values);
1768 string retnodename = null;
1769 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
1770 retnodename = returnisreturn ? "returns" : "value";
1771 string retnodename_other = !returnisreturn ? "returns" : "value";
1773 // If it has a returns node instead of a value node, change its name.
1774 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1775 if (retother != null) {
1776 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1777 foreach (XmlNode node in retother)
1778 retnode.AppendChild(node.CloneNode(true));
1779 e.ReplaceChild(retnode, retother);
1781 WriteElementInitialText(e, retnodename, "To be added.");
1784 ClearElement(e, "returns");
1785 ClearElement(e, "value");
1789 WriteElementInitialText(e, "remarks", "To be added.");
1791 if (exceptions.HasValue && info.Member != null &&
1792 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
1793 UpdateExceptions (e, info.Member);
1796 if (info.EcmaDocs != null) {
1797 XmlReader r = info.EcmaDocs;
1798 int depth = r.Depth;
1799 r.ReadStartElement ("Docs");
1801 if (r.Name == "Docs") {
1802 if (r.Depth == depth && r.NodeType == XmlNodeType.EndElement)
1805 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
1807 if (!r.IsStartElement ())
1812 string name = r.GetAttribute ("name");
1815 XmlNode doc = e.SelectSingleNode (
1816 r.Name + "[@name='" + name + "']");
1817 string value = r.ReadInnerXml ();
1819 doc.InnerXml = value.Replace ("\r", "");
1826 string name = r.Name;
1827 string cref = r.GetAttribute ("cref");
1830 XmlNode doc = e.SelectSingleNode (
1831 r.Name + "[@cref='" + cref + "']");
1832 string value = r.ReadInnerXml ().Replace ("\r", "");
1834 doc.InnerXml = value;
1836 XmlElement n = e.OwnerDocument.CreateElement (name);
1837 n.SetAttribute ("cref", cref);
1844 string name = r.Name;
1845 string xpath = r.Name;
1846 StringList attributes = new StringList (r.AttributeCount);
1847 if (r.MoveToFirstAttribute ()) {
1849 attributes.Add ("@" + r.Name + "=\"" + r.Value + "\"");
1850 } while (r.MoveToNextAttribute ());
1853 if (attributes.Count > 0) {
1854 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
1856 XmlNode doc = e.SelectSingleNode (xpath);
1857 string value = r.ReadInnerXml ().Replace ("\r", "");
1859 doc.InnerXml = value;
1862 XmlElement n = e.OwnerDocument.CreateElement (name);
1864 foreach (string a in attributes) {
1865 int eq = a.IndexOf ('=');
1866 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
1875 if (info.SlashDocs != null) {
1876 XmlNode elem = info.SlashDocs;
1878 if (elem.SelectSingleNode("summary") != null)
1879 ClearElement(e, "summary");
1880 if (elem.SelectSingleNode("remarks") != null)
1881 ClearElement(e, "remarks");
1882 if (elem.SelectSingleNode("value") != null)
1883 ClearElement(e, "value");
1884 if (retnodename != null && elem.SelectSingleNode(retnodename) != null)
1885 ClearElement(e, retnodename);
1887 foreach (XmlNode child in elem.ChildNodes) {
1888 switch (child.Name) {
1891 XmlAttribute name = child.Attributes ["name"];
1894 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
1896 p2.InnerXml = child.InnerXml;
1901 case "permission": {
1902 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
1905 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
1907 a = e.OwnerDocument.CreateElement (child.Name);
1908 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
1911 a.InnerXml = child.InnerXml;
1915 XmlAttribute cref = child.Attributes ["cref"];
1918 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
1920 a = e.OwnerDocument.CreateElement ("altmember");
1921 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
1927 CopyNode (child, e);
1934 OrderDocsNodes (e, e.ChildNodes);
1935 NormalizeWhitespace(e);
1938 static readonly string[] DocsNodeOrder = {
1939 "typeparam", "param", "summary", "returns", "value", "remarks",
1942 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
1944 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1945 for (int i = 0; i < DocsNodeOrder.Length; ++i) {
1946 for (int j = 0; j < children.Count; ++j) {
1947 XmlNode c = children [j];
1948 if (c.Name == DocsNodeOrder [i]) {
1949 newChildren.Add (c);
1953 if (newChildren.Count >= 0)
1954 docs.PrependChild ((XmlNode) newChildren [0]);
1955 for (int i = 1; i < newChildren.Count; ++i) {
1956 XmlNode prev = (XmlNode) newChildren [i-1];
1957 XmlNode cur = (XmlNode) newChildren [i];
1958 docs.RemoveChild (cur);
1959 docs.InsertAfter (cur, prev);
1964 private void UpdateParameters (XmlElement e, string element, string[] values)
1966 if (values != null) {
1967 XmlNode[] paramnodes = new XmlNode[values.Length];
1969 // Some documentation had param nodes with leading spaces.
1970 foreach (XmlElement paramnode in e.SelectNodes(element)){
1971 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
1974 // If a member has only one parameter, we can track changes to
1975 // the name of the parameter easily.
1976 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
1977 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
1980 bool reinsert = false;
1982 // Pick out existing and still-valid param nodes, and
1983 // create nodes for parameters not in the file.
1984 Hashtable seenParams = new Hashtable();
1985 for (int pi = 0; pi < values.Length; pi++) {
1986 string p = values [pi];
1989 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
1990 if (paramnodes[pi] != null) continue;
1992 XmlElement pe = e.OwnerDocument.CreateElement(element);
1993 pe.SetAttribute("name", p);
1994 pe.InnerText = "To be added.";
1995 paramnodes[pi] = pe;
1999 // Remove parameters that no longer exist and check all params are in the right order.
2001 MyXmlNodeList todelete = new MyXmlNodeList ();
2002 foreach (XmlElement paramnode in e.SelectNodes(element)) {
2003 string name = paramnode.GetAttribute("name");
2004 if (!seenParams.ContainsKey(name)) {
2005 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2006 Warning ("The following param node can only be deleted if the --delete option is given: ");
2007 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
2009 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
2010 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2014 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
2015 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2016 e.ParentNode.Attributes ["MemberName"].Value,
2019 Warning ("\tValue={0}", paramnode.OuterXml);
2021 todelete.Add (paramnode);
2026 if ((int)seenParams[name] != idx)
2032 foreach (XmlNode n in todelete) {
2033 n.ParentNode.RemoveChild (n);
2036 // Re-insert the parameter nodes at the top of the doc section.
2038 for (int pi = values.Length-1; pi >= 0; pi--)
2039 e.PrependChild(paramnodes[pi]);
2041 // Clear all existing param nodes
2042 foreach (XmlNode paramnode in e.SelectNodes(element)) {
2043 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2044 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
2045 Console.WriteLine(paramnode.OuterXml);
2047 paramnode.ParentNode.RemoveChild(paramnode);
2053 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
2055 string existingName = pe.GetAttribute ("name");
2056 pe.SetAttribute ("name", newName);
2057 if (existingName == newName)
2059 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
2060 if (paramref.GetAttribute ("name").Trim () == existingName)
2061 paramref.SetAttribute ("name", newName);
2064 class CrefComparer : XmlNodeComparer {
2066 public CrefComparer ()
2070 public override int Compare (XmlNode x, XmlNode y)
2072 string xType = x.Attributes ["cref"].Value;
2073 string yType = y.Attributes ["cref"].Value;
2074 string xNamespace = GetNamespace (xType);
2075 string yNamespace = GetNamespace (yType);
2077 int c = xNamespace.CompareTo (yNamespace);
2080 return xType.CompareTo (yType);
2083 static string GetNamespace (string type)
2085 int n = type.LastIndexOf ('.');
2087 return type.Substring (0, n);
2088 return string.Empty;
2092 private void UpdateExceptions (XmlNode docs, IMemberReference member)
2094 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
2095 string cref = slashdocFormatter.GetDeclaration (source.Exception);
2096 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
2099 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
2100 e.SetAttribute ("cref", cref);
2101 e.InnerXml = "To be added; from: <see cref=\"" +
2102 string.Join ("\" />, <see cref=\"",
2103 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
2106 docs.AppendChild (e);
2108 SortXmlNodes (docs, docs.SelectNodes ("exception"),
2109 new CrefComparer ());
2112 private static void NormalizeWhitespace(XmlElement e) {
2113 // Remove all text and whitespace nodes from the element so it
2114 // is outputted with nice indentation and no blank lines.
2115 ArrayList deleteNodes = new ArrayList();
2116 foreach (XmlNode n in e)
2117 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2119 foreach (XmlNode n in deleteNodes)
2120 n.ParentNode.RemoveChild(n);
2123 private static bool UpdateAssemblyVersions (XmlElement root, IMemberReference member, bool add)
2125 TypeDefinition type = member as TypeDefinition;
2127 type = member.DeclaringType as TypeDefinition;
2128 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
2131 private static string GetAssemblyVersion (AssemblyDefinition assembly)
2133 return assembly.Name.Version.ToString();
2136 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
2138 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
2140 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2141 root.AppendChild(e);
2143 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
2144 .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
2146 // matches.Count > 0 && add: ignore -- already present
2147 if (matches.Count > 0 && !add) {
2148 foreach (XmlNode c in matches)
2151 else if (matches.Count == 0 && add) {
2152 foreach (string sv in assemblyVersions) {
2153 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2158 // matches.Count == 0 && !add: ignore -- already not present
2160 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2161 SortXmlNodes (e, avs, new VersionComparer ());
2163 return avs.Count != 0;
2166 // FIXME: get TypeReferences instead of string comparison?
2167 private static string[] IgnorableAttributes = {
2168 // Security related attributes
2169 "System.Reflection.AssemblyKeyFileAttribute",
2170 "System.Reflection.AssemblyDelaySignAttribute",
2171 // Present in @RefType
2172 "System.Runtime.InteropServices.OutAttribute",
2173 // For naming the indexer to use when not using indexers
2174 "System.Reflection.DefaultMemberAttribute",
2175 // for decimal constants
2176 "System.Runtime.CompilerServices.DecimalConstantAttribute",
2177 // compiler generated code
2178 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
2179 // more compiler generated code, e.g. iterator methods
2180 "System.Diagnostics.DebuggerHiddenAttribute",
2181 "System.Runtime.CompilerServices.FixedBufferAttribute",
2182 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
2183 // extension methods
2184 "System.Runtime.CompilerServices.ExtensionAttribute",
2188 enum AttributeFlags {
2190 KeepExistingAttributes = 0x1,
2193 private void MakeAttributes (XmlElement root, CustomAttributeCollection attributes, AttributeFlags flags)
2195 MakeAttributes (root, attributes, flags, null);
2198 private void MakeAttributes (XmlElement root, CustomAttributeCollection attributes, AttributeFlags flags, string prefix)
2200 bool keepExisting = (flags & AttributeFlags.KeepExistingAttributes) != 0;
2201 if (attributes.Count == 0) {
2203 ClearElement(root, "Attributes");
2208 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2209 if (e != null && !keepExisting)
2212 e = root.OwnerDocument.CreateElement("Attributes");
2214 foreach (CustomAttribute attribute in attributes.Cast<CustomAttribute> ()
2215 .OrderBy (ca => ca.Constructor.DeclaringType.FullName)) {
2216 if (!attribute.Resolve ()) {
2218 Warning ("warning: could not resolve type {0}.",
2219 attribute.Constructor.DeclaringType.FullName);
2221 TypeDefinition attrType = attribute.Constructor.DeclaringType as TypeDefinition;
2222 if (attrType != null && !IsPublic (attrType))
2224 if (slashdocFormatter.GetName (attribute.Constructor.DeclaringType) == null)
2227 if (Array.IndexOf (IgnorableAttributes, attribute.Constructor.DeclaringType.FullName) >= 0)
2232 StringList fields = new StringList ();
2234 ParameterDefinitionCollection parameters = attribute.Constructor.Parameters;
2235 for (int i = 0; i < attribute.ConstructorParameters.Count; ++i) {
2236 fields.Add (MakeAttributesValueString (
2237 attribute.ConstructorParameters [i],
2238 parameters [i].ParameterType));
2241 (from de in attribute.Fields.Cast<DictionaryEntry> ()
2242 select new { Type=attribute.GetFieldType (de.Key.ToString ()), Name=de.Key, Value=de.Value })
2244 (from de in attribute.Properties.Cast<DictionaryEntry> ()
2245 select new { Type=attribute.GetPropertyType (de.Key.ToString ()), Name=de.Key, Value=de.Value }))
2246 .OrderBy (v => v.Name);
2247 foreach (var d in namedArgs)
2248 fields.Add (string.Format ("{0}={1}", d.Name,
2249 MakeAttributesValueString (d.Value, d.Type)));
2251 string a2 = String.Join(", ", fields.ToArray ());
2252 if (a2 != "") a2 = "(" + a2 + ")";
2254 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2257 string name = attribute.Constructor.DeclaringType.FullName;
2258 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
2259 WriteElementText(ae, "AttributeName", prefix + name + a2);
2262 if (b && e.ParentNode == null)
2263 root.AppendChild(e);
2265 ClearElement(root, "Attributes");
2267 NormalizeWhitespace(e);
2270 private static string MakeAttributesValueString (object v, TypeReference valueType)
2274 if (valueType.FullName == "System.Type")
2275 return "typeof(" + v.ToString () + ")";
2276 if (valueType.FullName == "System.String")
2277 return "\"" + v.ToString () + "\"";
2279 return (bool)v ? "true" : "false";
2280 TypeDefinition valueDef = valueType.Resolve ();
2281 if (valueDef == null || !valueDef.IsEnum)
2282 return v.ToString ();
2283 string typename = GetDocTypeFullName (valueType);
2284 var values = GetEnumerationValues (valueDef);
2285 ulong c = Convert.ToUInt64 (v);
2286 if (values.ContainsKey (c))
2287 return typename + "." + values [c];
2288 if (valueDef.CustomAttributes.Cast<CustomAttribute> ()
2289 .Any (ca => ca.Constructor.DeclaringType.FullName == "System.FlagsAttribute")) {
2290 return string.Join (" | ",
2291 (from i in values.Keys
2293 select typename + "." + values [i])
2296 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
2299 private static Dictionary<ulong, string> GetEnumerationValues (TypeDefinition type)
2301 var values = new Dictionary<ulong, string> ();
2303 (from f in type.Fields.Cast<FieldDefinition> ()
2304 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
2306 values [Convert.ToUInt64 (f.Constant)] = f.Name;
2311 private void MakeParameters (XmlElement root, ParameterDefinitionCollection parameters)
2313 XmlElement e = WriteElement(root, "Parameters");
2315 foreach (ParameterDefinition p in parameters) {
2316 XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
2318 pe.SetAttribute("Name", p.Name);
2319 pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
2320 if (p.ParameterType is ReferenceType) {
2321 if (p.IsOut) pe.SetAttribute("RefType", "out");
2322 else pe.SetAttribute("RefType", "ref");
2324 MakeAttributes (pe, p.CustomAttributes, 0);
2328 private void MakeTypeParameters (XmlElement root, GenericParameterCollection typeParams)
2330 if (typeParams == null || typeParams.Count == 0) {
2331 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2333 root.RemoveChild (f);
2336 XmlElement e = WriteElement(root, "TypeParameters");
2338 foreach (GenericParameter t in typeParams) {
2339 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2341 pe.SetAttribute("Name", t.Name);
2342 MakeAttributes (pe, t.CustomAttributes, 0);
2343 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2344 ConstraintCollection constraints = t.Constraints;
2345 GenericParameterAttributes attrs = t.Attributes;
2346 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2354 ce = root.OwnerDocument.CreateElement ("Constraints");
2356 pe.AppendChild (ce);
2357 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2358 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2359 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2360 AppendElementText (ce, "ParameterAttribute", "Covariant");
2361 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2362 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2363 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2364 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2365 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2366 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2367 foreach (TypeReference c in constraints) {
2368 TypeDefinition cd = c.Resolve ();
2369 AppendElementText (ce,
2370 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2371 GetDocTypeFullName (c));
2376 private void MakeParameters (XmlElement root, IMemberReference mi)
2378 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2379 MakeParameters (root, ((MethodDefinition)mi).Parameters);
2380 else if (mi is MethodDefinition) {
2381 MethodDefinition mb = (MethodDefinition) mi;
2382 ParameterDefinitionCollection parameters = mb.Parameters;
2383 MakeParameters(root, parameters);
2384 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2385 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2386 p.SetAttribute ("RefType", "this");
2389 else if (mi is PropertyDefinition) {
2390 ParameterDefinitionCollection parameters = ((PropertyDefinition)mi).Parameters;
2391 if (parameters.Count > 0)
2392 MakeParameters(root, parameters);
2396 else if (mi is FieldDefinition) return;
2397 else if (mi is EventDefinition) return;
2398 else throw new ArgumentException();
2401 private static string GetDocParameterType (TypeReference type)
2403 return GetDocTypeFullName (type).Replace ("@", "&");
2406 private void MakeReturnValue (XmlElement root, TypeReference type, CustomAttributeCollection attributes)
2408 XmlElement e = WriteElement(root, "ReturnValue");
2410 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2411 if (attributes != null)
2412 MakeAttributes(e, attributes, 0);
2415 private void MakeReturnValue (XmlElement root, IMemberReference mi)
2417 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2419 else if (mi is MethodDefinition)
2420 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType.ReturnType, ((MethodDefinition)mi).ReturnType.CustomAttributes);
2421 else if (mi is PropertyDefinition)
2422 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
2423 else if (mi is FieldDefinition)
2424 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
2425 else if (mi is EventDefinition)
2426 MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
2428 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2431 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2433 IMemberReference mi = info.Member;
2434 if (mi is TypeDefinition) return null;
2436 string sigs = MakeMemberSignature(mi);
2437 if (sigs == null) return null; // not publicly visible
2439 // no documentation for property/event accessors. Is there a better way of doing this?
2440 if (mi.Name.StartsWith("get_")) return null;
2441 if (mi.Name.StartsWith("set_")) return null;
2442 if (mi.Name.StartsWith("add_")) return null;
2443 if (mi.Name.StartsWith("remove_")) return null;
2444 if (mi.Name.StartsWith("raise_")) return null;
2446 XmlElement me = doc.CreateElement("Member");
2447 me.SetAttribute("MemberName", GetMemberName (mi));
2451 if (exceptions.HasValue &&
2452 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2453 UpdateExceptions (info.Node, info.Member);
2455 if (since != null) {
2456 XmlNode docs = me.SelectSingleNode("Docs");
2457 docs.AppendChild (CreateSinceNode (doc));
2463 private static string GetMemberName (IMemberReference mi)
2465 MethodDefinition mb = mi as MethodDefinition;
2467 PropertyDefinition pi = mi as PropertyDefinition;
2470 return DocUtils.GetPropertyName (pi);
2472 StringBuilder sb = new StringBuilder (mi.Name.Length);
2473 if (!DocUtils.IsExplicitlyImplemented (mb))
2474 sb.Append (mi.Name);
2476 TypeReference iface;
2477 MethodReference ifaceMethod;
2478 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2479 sb.Append (GetDocTypeFullName (iface));
2481 sb.Append (ifaceMethod.Name);
2483 if (mb.IsGenericMethod ()) {
2484 GenericParameterCollection typeParams = mb.GenericParameters;
2485 if (typeParams.Count > 0) {
2487 sb.Append (typeParams [0].Name);
2488 for (int i = 1; i < typeParams.Count; ++i)
2489 sb.Append (",").Append (typeParams [i].Name);
2493 return sb.ToString ();
2496 private static int CountChars (string s, char c)
2499 for (int i = 0; i < s.Length; ++i) {
2506 /// SIGNATURE GENERATION FUNCTIONS
2508 static string MakeTypeSignature (TypeReference type)
2510 return csharpFormatter.GetDeclaration (type);
2513 static string MakeMemberSignature (IMemberReference mi)
2515 return csharpFullFormatter.GetDeclaration (mi);
2518 static string GetMemberType (IMemberReference mi)
2520 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2521 return "Constructor";
2522 if (mi is MethodDefinition)
2524 if (mi is PropertyDefinition)
2526 if (mi is FieldDefinition)
2528 if (mi is EventDefinition)
2530 throw new ArgumentException();
2533 private static string GetDocTypeName (TypeReference type)
2535 return docTypeFormatter.GetName (type);
2538 private static string GetDocTypeFullName (TypeReference type)
2540 return DocTypeFullMemberFormatter.Default.GetName (type);
2543 class DocsNodeInfo {
2544 public DocsNodeInfo (XmlElement node)
2549 public DocsNodeInfo (XmlElement node, TypeDefinition type)
2555 public DocsNodeInfo (XmlElement node, IMemberReference member)
2558 SetMemberInfo (member);
2561 void SetType (TypeDefinition type)
2564 throw new ArgumentNullException ("type");
2565 GenericParameters = new List<GenericParameter> (type.GenericParameters.Cast<GenericParameter> ());
2566 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
2567 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
2568 for (int i = 0; i < declTypes.Count - 1; ++i) {
2569 int remove = System.Math.Min (maxGenArgs,
2570 DocUtils.GetGenericArgumentCount (declTypes [i]));
2571 maxGenArgs -= remove;
2572 while (remove-- > 0)
2573 GenericParameters.RemoveAt (0);
2575 if (DocUtils.IsDelegate (type)) {
2576 Parameters = type.GetMethod("Invoke").Parameters;
2577 ReturnType = type.GetMethod("Invoke").ReturnType.ReturnType;
2579 SetSlashDocs (type);
2582 void SetMemberInfo (IMemberReference member)
2585 throw new ArgumentNullException ("member");
2586 ReturnIsReturn = true;
2590 if (member is MethodReference ) {
2591 MethodReference mr = (MethodReference) member;
2592 Parameters = mr.Parameters;
2593 if (mr.IsGenericMethod ()) {
2594 GenericParameters = new List<GenericParameter> (mr.GenericParameters.Cast<GenericParameter> ());
2597 else if (member is PropertyDefinition) {
2598 Parameters = ((PropertyDefinition) member).Parameters;
2601 if (member is MethodDefinition) {
2602 ReturnType = ((MethodDefinition) member).ReturnType.ReturnType;
2603 } else if (member is PropertyDefinition) {
2604 ReturnType = ((PropertyDefinition) member).PropertyType;
2605 ReturnIsReturn = false;
2608 // no remarks section for enum members
2609 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
2611 SetSlashDocs (member);
2614 private void SetSlashDocs (IMemberReference member)
2616 if (slashdocs == null)
2619 string slashdocsig = slashdocFormatter.GetDeclaration (member);
2620 if (slashdocsig != null)
2621 SlashDocs = slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
2624 public TypeReference ReturnType;
2625 public List<GenericParameter> GenericParameters;
2626 public ParameterDefinitionCollection Parameters;
2627 public bool ReturnIsReturn;
2628 public XmlElement Node;
2629 public bool AddRemarks = true;
2630 public XmlNode SlashDocs;
2631 public XmlReader EcmaDocs;
2632 public IMemberReference Member;
2635 static string GetXPathForMember (DocumentationMember member)
2637 StringBuilder xpath = new StringBuilder ();
2638 xpath.Append ("//Members/Member[@MemberName=\"")
2639 .Append (member.MemberName)
2641 if (member.Parameters != null && member.Parameters.Count > 0) {
2642 xpath.Append ("/Parameters[count(Parameter) = ")
2643 .Append (member.Parameters.Count);
2644 for (int i = 0; i < member.Parameters.Count; ++i) {
2645 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2646 xpath.Append (member.Parameters [i]);
2647 xpath.Append ("\"");
2649 xpath.Append ("]/..");
2651 return xpath.ToString ();
2654 public static string GetXPathForMember (XPathNavigator member)
2656 StringBuilder xpath = new StringBuilder ();
2657 xpath.Append ("//Type[@FullName=\"")
2658 .Append (member.SelectSingleNode ("../../@FullName").Value)
2660 xpath.Append ("Members/Member[@MemberName=\"")
2661 .Append (member.SelectSingleNode ("@MemberName").Value)
2663 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2664 if (parameters.Count > 0) {
2665 xpath.Append ("/Parameters[count(Parameter) = ")
2666 .Append (parameters.Count);
2668 while (parameters.MoveNext ()) {
2670 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2671 xpath.Append (parameters.Current.Value);
2672 xpath.Append ("\"");
2674 xpath.Append ("]/..");
2676 return xpath.ToString ();
2679 public static string GetXPathForMember (IMemberReference member)
2681 StringBuilder xpath = new StringBuilder ();
2682 xpath.Append ("//Type[@FullName=\"")
2683 .Append (member.DeclaringType.FullName)
2685 xpath.Append ("Members/Member[@MemberName=\"")
2686 .Append (GetMemberName (member))
2689 ParameterDefinitionCollection parameters = null;
2690 if (member is MethodDefinition)
2691 parameters = ((MethodDefinition) member).Parameters;
2692 else if (member is PropertyDefinition) {
2693 parameters = ((PropertyDefinition) member).Parameters;
2695 if (parameters != null && parameters.Count > 0) {
2696 xpath.Append ("/Parameters[count(Parameter) = ")
2697 .Append (parameters.Count);
2698 for (int i = 0; i < parameters.Count; ++i) {
2699 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2700 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2701 xpath.Append ("\"");
2703 xpath.Append ("]/..");
2705 return xpath.ToString ();
2709 static class CecilExtensions {
2710 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type)
2712 foreach (var c in type.Constructors)
2713 yield return (IMemberReference) c;
2714 foreach (var e in type.Events)
2715 yield return (IMemberReference) e;
2716 foreach (var f in type.Fields)
2717 yield return (IMemberReference) f;
2718 foreach (var m in type.Methods)
2719 yield return (IMemberReference) m;
2720 foreach (var t in type.NestedTypes)
2721 yield return (IMemberReference) t;
2722 foreach (var p in type.Properties)
2723 yield return (IMemberReference) p;
2726 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type, string member)
2728 return GetMembers (type).Where (m => m.Name == member);
2731 public static IMemberReference GetMember (this TypeDefinition type, string member)
2733 return GetMembers (type, member).EnsureZeroOrOne ();
2736 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2738 if (source.Count () > 1)
2739 throw new InvalidOperationException ("too many matches");
2740 return source.FirstOrDefault ();
2743 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2745 return type.Methods.Cast<MethodDefinition> ()
2746 .Where (m => m.Name == method)
2747 .EnsureZeroOrOne ();
2750 public static IEnumerable<IMemberReference> GetDefaultMembers (this TypeReference type)
2752 TypeDefinition def = type as TypeDefinition;
2754 return new IMemberReference [0];
2755 CustomAttribute defMemberAttr = type.CustomAttributes.Cast<CustomAttribute> ()
2756 .Where (c => c.Constructor.DeclaringType.FullName == "System.Reflection.DefaultMemberAttribute")
2758 if (defMemberAttr == null)
2759 return new IMemberReference [0];
2760 string name = (string) defMemberAttr.ConstructorParameters [0];
2761 return def.Properties.Cast<PropertyDefinition> ()
2762 .Where (p => p.Name == name)
2763 .Select (p => (IMemberReference) p);
2766 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2768 return assembly.Modules.Cast<ModuleDefinition> ()
2769 .SelectMany (md => md.Types.Cast<TypeDefinition> ());
2772 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2774 return GetTypes (assembly)
2775 .Where (td => td.FullName == type)
2776 .EnsureZeroOrOne ();
2779 public static bool IsGenericType (this TypeReference type)
2781 return type.GenericParameters.Count > 0;
2784 public static bool IsGenericMethod (this MethodReference method)
2786 return method.GenericParameters.Count > 0;
2789 public static IMemberReference Resolve (this IMemberReference member)
2791 EventReference er = member as EventReference;
2793 return er.Resolve ();
2794 FieldReference fr = member as FieldReference;
2796 return fr.Resolve ();
2797 MethodReference mr = member as MethodReference;
2799 return mr.Resolve ();
2800 PropertyReference pr = member as PropertyReference;
2802 return pr.Resolve ();
2803 TypeReference tr = member as TypeReference;
2805 return tr.Resolve ();
2806 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2810 static class DocUtils {
2811 public static bool IsExplicitlyImplemented (MethodDefinition method)
2813 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2816 public static string GetTypeDotMember (string name)
2818 int startType, startMethod;
2819 startType = startMethod = -1;
2820 for (int i = 0; i < name.Length; ++i) {
2821 if (name [i] == '.') {
2822 startType = startMethod;
2826 return name.Substring (startType+1);
2829 public static string GetMember (string name)
2831 int i = name.LastIndexOf ('.');
2834 return name.Substring (i+1);
2837 public static void GetInfoForExplicitlyImplementedMethod (
2838 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2842 if (method.Overrides.Count != 1)
2843 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2844 iface = method.Overrides [0].DeclaringType;
2845 ifaceMethod = method.Overrides [0];
2848 public static string GetPropertyName (PropertyDefinition pi)
2850 // Issue: (g)mcs-generated assemblies that explicitly implement
2851 // properties don't specify the full namespace, just the
2852 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2853 MethodDefinition method = pi.GetMethod;
2855 method = pi.SetMethod;
2856 if (!IsExplicitlyImplemented (method))
2859 // Need to determine appropriate namespace for this member.
2860 TypeReference iface;
2861 MethodReference ifaceMethod;
2862 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2863 return string.Join (".", new string[]{
2864 DocTypeFullMemberFormatter.Default.GetName (iface),
2865 GetMember (pi.Name)});
2868 public static string GetNamespace (TypeReference type)
2870 if (type.GetOriginalType ().IsNested)
2871 type = type.GetOriginalType ();
2872 while (type != null && type.IsNested)
2873 type = type.DeclaringType;
2875 return string.Empty;
2876 return type.Namespace;
2879 public static string PathCombine (string dir, string path)
2885 return Path.Combine (dir, path);
2888 public static bool IsExtensionMethod (MethodDefinition method)
2891 method.CustomAttributes.Cast<CustomAttribute> ()
2892 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2894 method.DeclaringType.CustomAttributes.Cast<CustomAttribute> ()
2895 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2899 public static bool IsDelegate (TypeDefinition type)
2901 TypeReference baseRef = type.BaseType;
2902 if (baseRef == null)
2904 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2905 baseRef.FullName == "System.MulticastDelegate";
2908 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2910 List<TypeReference> decls = new List<TypeReference> ();
2912 while (type.DeclaringType != null) {
2913 decls.Add (type.DeclaringType);
2914 type = type.DeclaringType;
2920 public static int GetGenericArgumentCount (TypeReference type)
2922 GenericInstanceType inst = type as GenericInstanceType;
2924 ? inst.GenericArguments.Count
2925 : type.GenericParameters.Count;
2928 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
2930 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
2931 List<TypeReference> userInterfaces = new List<TypeReference> ();
2932 foreach (TypeReference iface in type.Interfaces) {
2933 TypeReference lookup = iface.Resolve () ?? iface;
2934 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
2935 userInterfaces.Add (iface);
2937 return userInterfaces;
2940 private static string GetQualifiedTypeName (TypeReference type)
2942 return "[" + type.Scope.Name + "]" + type.FullName;
2945 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
2947 HashSet<string> inheritedInterfaces = new HashSet<string> ();
2948 Action<TypeDefinition> a = null;
2950 if (t == null) return;
2951 foreach (TypeReference r in t.Interfaces) {
2952 inheritedInterfaces.Add (GetQualifiedTypeName (r));
2956 TypeReference baseRef = type.BaseType;
2957 while (baseRef != null) {
2958 TypeDefinition baseDef = baseRef.Resolve ();
2959 if (baseDef != null) {
2961 baseRef = baseDef.BaseType;
2966 foreach (TypeReference r in type.Interfaces)
2968 return inheritedInterfaces;
2972 class DocumentationMember {
2973 public StringToStringMap MemberSignatures = new StringToStringMap ();
2974 public string ReturnType;
2975 public StringList Parameters;
2976 public string MemberName;
2977 public string MemberType;
2979 public DocumentationMember (XmlReader reader)
2981 MemberName = reader.GetAttribute ("MemberName");
2982 int depth = reader.Depth;
2984 StringList p = new StringList ();
2986 if (reader.NodeType != XmlNodeType.Element)
2988 switch (reader.Name) {
2989 case "MemberSignature":
2990 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
2993 MemberType = reader.ReadElementString ();
2996 if (reader.Depth == depth + 2)
2997 ReturnType = reader.ReadElementString ();
3000 if (reader.Depth == depth + 2)
3001 p.Add (reader.GetAttribute ("Type"));
3004 if (reader.Depth == depth + 1)
3008 } while (go && reader.Read () && reader.Depth >= depth);
3014 public DocumentationMember (XmlNode node)
3016 MemberName = node.Attributes ["MemberName"].Value;
3017 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3018 XmlAttribute l = n.Attributes ["Language"];
3019 XmlAttribute v = n.Attributes ["Value"];
3020 if (l != null && v != null)
3021 MemberSignatures [l.Value] = v.Value;
3023 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3024 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
3026 ReturnType = rt.InnerText;
3027 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
3029 Parameters = new StringList (p.Count);
3030 for (int i = 0; i < p.Count; ++i)
3031 Parameters.Add (p [i].Attributes ["Type"].Value);
3036 public enum MemberFormatterState {
3039 WithinGenericTypeContainer,
3042 public abstract class MemberFormatter {
3043 public virtual string GetName (IMemberReference member)
3045 TypeReference type = member as TypeReference;
3047 return GetTypeName (type);
3048 MethodReference method = member as MethodReference;
3049 if (method != null && method.Name == ".ctor") // method.IsConstructor
3050 return GetConstructorName (method);
3052 return GetMethodName (method);
3053 PropertyReference prop = member as PropertyReference;
3055 return GetPropertyName (prop);
3056 FieldReference field = member as FieldReference;
3058 return GetFieldName (field);
3059 EventReference e = member as EventReference;
3061 return GetEventName (e);
3062 throw new NotSupportedException ("Can't handle: " +
3063 (member == null ? "null" : member.GetType().ToString()));
3066 protected virtual string GetTypeName (TypeReference type)
3069 throw new ArgumentNullException ("type");
3070 return _AppendTypeName (new StringBuilder (type.Name.Length), type).ToString ();
3073 protected virtual char[] ArrayDelimeters {
3074 get {return new char[]{'[', ']'};}
3077 protected virtual MemberFormatterState MemberFormatterState { get; set; }
3079 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type)
3081 if (type is ArrayType) {
3082 TypeSpecification spec = type as TypeSpecification;
3083 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3084 .Append (ArrayDelimeters [0]);
3085 var origState = MemberFormatterState;
3086 MemberFormatterState = MemberFormatterState.WithinArray;
3087 ArrayType array = (ArrayType) type;
3088 int rank = array.Rank;
3090 buf.Append (new string (',', rank-1));
3091 MemberFormatterState = origState;
3092 return buf.Append (ArrayDelimeters [1]);
3094 if (type is ReferenceType) {
3095 return AppendRefTypeName (buf, type);
3097 if (type is PointerType) {
3098 return AppendPointerTypeName (buf, type);
3100 AppendNamespace (buf, type);
3101 if (type is GenericParameter) {
3102 return AppendTypeName (buf, type);
3104 GenericInstanceType genInst = type as GenericInstanceType;
3105 if (type.GenericParameters.Count == 0 &&
3106 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3107 return AppendFullTypeName (buf, type);
3109 return AppendGenericType (buf, type);
3112 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3114 string ns = DocUtils.GetNamespace (type);
3115 if (ns != null && ns.Length > 0)
3116 buf.Append (ns).Append ('.');
3120 private StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type)
3122 if (type.DeclaringType != null)
3123 AppendFullTypeName (buf, type.DeclaringType).Append (NestedTypeSeparator);
3124 return AppendTypeName (buf, type);
3127 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3129 return AppendTypeName (buf, type.Name);
3132 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3134 int n = typename.IndexOf ("`");
3136 return buf.Append (typename.Substring (0, n));
3137 return buf.Append (typename);
3140 protected virtual string RefTypeModifier {
3144 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type)
3146 TypeSpecification spec = type as TypeSpecification;
3147 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3148 .Append (RefTypeModifier);
3151 protected virtual string PointerModifier {
3155 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type)
3157 TypeSpecification spec = type as TypeSpecification;
3158 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3159 .Append (PointerModifier);
3162 protected virtual char[] GenericTypeContainer {
3163 get {return new char[]{'<', '>'};}
3166 protected virtual char NestedTypeSeparator {
3170 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3172 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3173 type is GenericInstanceType ? type.GetOriginalType () : type);
3174 List<TypeReference> genArgs = GetGenericArguments (type);
3177 bool insertNested = false;
3178 foreach (var decl in decls) {
3179 TypeReference declDef = decl.Resolve () ?? decl;
3181 buf.Append (NestedTypeSeparator);
3183 insertNested = true;
3184 AppendTypeName (buf, declDef);
3185 int ac = DocUtils.GetGenericArgumentCount (declDef);
3189 buf.Append (GenericTypeContainer [0]);
3190 var origState = MemberFormatterState;
3191 MemberFormatterState = MemberFormatterState.WithinGenericTypeContainer;
3192 _AppendTypeName (buf, genArgs [argIdx++]);
3193 for (int i = 1; i < c; ++i)
3194 _AppendTypeName (buf.Append (","), genArgs [argIdx++]);
3195 MemberFormatterState = origState;
3196 buf.Append (GenericTypeContainer [1]);
3202 private List<TypeReference> GetGenericArguments (TypeReference type)
3204 var args = new List<TypeReference> ();
3205 GenericInstanceType inst = type as GenericInstanceType;
3207 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
3209 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
3213 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3218 protected virtual string GetConstructorName (MethodReference constructor)
3220 return constructor.Name;
3223 protected virtual string GetMethodName (MethodReference method)
3228 protected virtual string GetPropertyName (PropertyReference property)
3230 return property.Name;
3233 protected virtual string GetFieldName (FieldReference field)
3238 protected virtual string GetEventName (EventReference e)
3243 public virtual string GetDeclaration (IMemberReference member)
3246 throw new ArgumentNullException ("member");
3247 TypeDefinition type = member as TypeDefinition;
3249 return GetTypeDeclaration (type);
3250 MethodDefinition method = member as MethodDefinition;
3251 if (method != null && method.IsConstructor)
3252 return GetConstructorDeclaration (method);
3254 return GetMethodDeclaration (method);
3255 PropertyDefinition prop = member as PropertyDefinition;
3257 return GetPropertyDeclaration (prop);
3258 FieldDefinition field = member as FieldDefinition;
3260 return GetFieldDeclaration (field);
3261 EventDefinition e = member as EventDefinition;
3263 return GetEventDeclaration (e);
3264 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3267 protected virtual string GetTypeDeclaration (TypeDefinition type)
3270 throw new ArgumentNullException ("type");
3271 StringBuilder buf = new StringBuilder (type.Name.Length);
3272 _AppendTypeName (buf, type);
3273 AppendGenericTypeConstraints (buf, type);
3274 return buf.ToString ();
3277 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
3279 return GetConstructorName (constructor);
3282 protected virtual string GetMethodDeclaration (MethodDefinition method)
3284 // Special signature for destructors.
3285 if (method.Name == "Finalize" && method.Parameters.Count == 0)
3286 return GetFinalizerName (method);
3288 StringBuilder buf = new StringBuilder ();
3290 AppendVisibility (buf, method);
3291 if (buf.Length == 0 &&
3292 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3295 AppendModifiers (buf, method);
3297 if (buf.Length != 0)
3299 buf.Append (GetName (method.ReturnType.ReturnType)).Append (" ");
3301 AppendMethodName (buf, method);
3302 AppendGenericMethod (buf, method).Append (" ");
3303 AppendParameters (buf, method, method.Parameters);
3304 AppendGenericMethodConstraints (buf, method);
3305 return buf.ToString ();
3308 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3310 return buf.Append (method.Name);
3313 protected virtual string GetFinalizerName (MethodDefinition method)
3318 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3323 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3328 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3333 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3338 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3343 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
3345 return GetPropertyName (property);
3348 protected virtual string GetFieldDeclaration (FieldDefinition field)
3350 return GetFieldName (field);
3353 protected virtual string GetEventDeclaration (EventDefinition e)
3355 return GetEventName (e);
3359 class CSharpFullMemberFormatter : MemberFormatter {
3361 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3363 string ns = DocUtils.GetNamespace (type);
3364 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
3365 buf.Append (ns).Append ('.');
3369 private string GetCSharpType (string t)
3372 case "System.Byte": return "byte";
3373 case "System.SByte": return "sbyte";
3374 case "System.Int16": return "short";
3375 case "System.Int32": return "int";
3376 case "System.Int64": return "long";
3378 case "System.UInt16": return "ushort";
3379 case "System.UInt32": return "uint";
3380 case "System.UInt64": return "ulong";
3382 case "System.Single": return "float";
3383 case "System.Double": return "double";
3384 case "System.Decimal": return "decimal";
3385 case "System.Boolean": return "bool";
3386 case "System.Char": return "char";
3387 case "System.Void": return "void";
3388 case "System.String": return "string";
3389 case "System.Object": return "object";
3394 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3396 if (type is GenericParameter)
3397 return AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
3398 string t = type.FullName;
3399 if (!t.StartsWith ("System.")) {
3400 return base.AppendTypeName (buf, type);
3403 string s = GetCSharpType (t);
3405 return buf.Append (s);
3407 return base.AppendTypeName (buf, type);
3410 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
3412 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeContainer)
3414 GenericParameterAttributes attrs = type.Attributes;
3415 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
3416 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
3420 buf.Append ("out ");
3424 protected override string GetTypeDeclaration (TypeDefinition type)
3426 string visibility = GetTypeVisibility (type.Attributes);
3427 if (visibility == null)
3430 StringBuilder buf = new StringBuilder ();
3432 buf.Append (visibility);
3435 MemberFormatter full = new CSharpFullMemberFormatter ();
3437 if (DocUtils.IsDelegate (type)) {
3438 buf.Append("delegate ");
3439 MethodDefinition invoke = type.GetMethod ("Invoke");
3440 buf.Append (full.GetName (invoke.ReturnType.ReturnType)).Append (" ");
3441 buf.Append (GetName (type));
3442 AppendParameters (buf, invoke, invoke.Parameters);
3443 AppendGenericTypeConstraints (buf, type);
3446 return buf.ToString();
3449 if (type.IsAbstract && !type.IsInterface)
3450 buf.Append("abstract ");
3451 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
3452 buf.Append("sealed ");
3453 buf.Replace ("abstract sealed", "static");
3455 buf.Append (GetTypeKind (type));
3457 buf.Append (GetCSharpType (type.FullName) == null
3462 TypeReference basetype = type.BaseType;
3463 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
3466 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
3467 .Select (iface => full.GetName (iface))
3471 if (basetype != null || interface_names.Count > 0)
3474 if (basetype != null) {
3475 buf.Append (full.GetName (basetype));
3476 if (interface_names.Count > 0)
3480 for (int i = 0; i < interface_names.Count; i++){
3483 buf.Append (interface_names [i]);
3485 AppendGenericTypeConstraints (buf, type);
3488 return buf.ToString ();
3491 static string GetTypeKind (TypeDefinition t)
3497 if (t.IsClass || t.FullName == "System.Enum")
3501 throw new ArgumentException(t.FullName);
3504 static string GetTypeVisibility (TypeAttributes ta)
3506 switch (ta & TypeAttributes.VisibilityMask) {
3507 case TypeAttributes.Public:
3508 case TypeAttributes.NestedPublic:
3511 case TypeAttributes.NestedFamily:
3512 case TypeAttributes.NestedFamORAssem:
3520 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3522 if (type.GenericParameters.Count == 0)
3524 return AppendConstraints (buf, type.GenericParameters);
3527 private StringBuilder AppendConstraints (StringBuilder buf, GenericParameterCollection genArgs)
3529 foreach (GenericParameter genArg in genArgs) {
3530 GenericParameterAttributes attrs = genArg.Attributes;
3531 ConstraintCollection constraints = genArg.Constraints;
3532 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
3535 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
3536 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
3537 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
3540 if (!isref && !isvt && !isnew && constraints.Count == 0)
3542 buf.Append (" where ").Append (genArg.Name).Append (" : ");
3544 buf.Append ("class");
3548 buf.Append ("struct");
3551 if (constraints.Count > 0 && !isvt) {
3554 buf.Append (GetTypeName (constraints [0]));
3555 for (int i = 1; i < constraints.Count; ++i)
3556 buf.Append (", ").Append (GetTypeName (constraints [i]));
3558 if (isnew && !isvt) {
3561 buf.Append ("new()");
3567 protected override string GetConstructorDeclaration (MethodDefinition constructor)
3569 StringBuilder buf = new StringBuilder ();
3570 AppendVisibility (buf, constructor);
3571 if (buf.Length == 0)
3575 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
3576 AppendParameters (buf, constructor, constructor.Parameters);
3579 return buf.ToString ();
3582 protected override string GetMethodDeclaration (MethodDefinition method)
3584 string decl = base.GetMethodDeclaration (method);
3590 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3592 if (DocUtils.IsExplicitlyImplemented (method)) {
3593 TypeReference iface;
3594 MethodReference ifaceMethod;
3595 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3596 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3598 .Append (ifaceMethod.Name);
3600 return base.AppendMethodName (buf, method);
3603 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3605 if (method.GenericParameters.Count == 0)
3607 return AppendConstraints (buf, method.GenericParameters);
3610 protected override string RefTypeModifier {
3614 protected override string GetFinalizerName (MethodDefinition method)
3616 return "~" + method.DeclaringType.Name + " ()";
3619 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3623 if (method.IsPublic)
3624 return buf.Append ("public");
3625 if (method.IsFamily || method.IsFamilyOrAssembly)
3626 return buf.Append ("protected");
3630 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3632 string modifiers = String.Empty;
3633 if (method.IsStatic) modifiers += " static";
3634 if (method.IsVirtual && !method.IsAbstract) {
3635 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3636 else modifiers += " override";
3638 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
3639 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
3640 if (method.IsFinal) modifiers += " sealed";
3641 if (modifiers == " virtual sealed") modifiers = "";
3643 return buf.Append (modifiers);
3646 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3648 if (method.IsGenericMethod ()) {
3649 GenericParameterCollection args = method.GenericParameters;
3650 if (args.Count > 0) {
3652 buf.Append (args [0].Name);
3653 for (int i = 1; i < args.Count; ++i)
3654 buf.Append (",").Append (args [i].Name);
3661 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3663 return AppendParameters (buf, method, parameters, '(', ')');
3666 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters, char begin, char end)
3670 if (parameters.Count > 0) {
3671 if (DocUtils.IsExtensionMethod (method))
3672 buf.Append ("this ");
3673 AppendParameter (buf, parameters [0]);
3674 for (int i = 1; i < parameters.Count; ++i) {
3676 AppendParameter (buf, parameters [i]);
3680 return buf.Append (end);
3683 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
3685 if (parameter.ParameterType is ReferenceType) {
3686 if (parameter.IsOut)
3687 buf.Append ("out ");
3689 buf.Append ("ref ");
3691 buf.Append (GetName (parameter.ParameterType)).Append (" ");
3692 return buf.Append (parameter.Name);
3695 protected override string GetPropertyDeclaration (PropertyDefinition property)
3697 MethodDefinition method;
3699 string get_visible = null;
3700 if ((method = property.GetMethod) != null &&
3701 (DocUtils.IsExplicitlyImplemented (method) ||
3702 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3703 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3704 string set_visible = null;
3705 if ((method = property.SetMethod) != null &&
3706 (DocUtils.IsExplicitlyImplemented (method) ||
3707 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3708 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3710 if ((set_visible == null) && (get_visible == null))
3714 StringBuilder buf = new StringBuilder ();
3715 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
3716 buf.Append (visibility = get_visible);
3717 else if (set_visible != null && get_visible == null)
3718 buf.Append (visibility = set_visible);
3720 buf.Append (visibility = "public");
3722 // Pick an accessor to use for static/virtual/override/etc. checks.
3723 method = property.SetMethod;
3725 method = property.GetMethod;
3727 string modifiers = String.Empty;
3728 if (method.IsStatic) modifiers += " static";
3729 if (method.IsVirtual && !method.IsAbstract) {
3730 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
3731 modifiers += " virtual";
3733 modifiers += " override";
3735 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
3736 if (method.IsAbstract && !declDef.IsInterface)
3737 modifiers += " abstract";
3739 modifiers += " sealed";
3740 if (modifiers == " virtual sealed")
3742 buf.Append (modifiers).Append (' ');
3744 buf.Append (GetName (property.PropertyType)).Append (' ');
3746 IEnumerable<IMemberReference> defs = property.DeclaringType.GetDefaultMembers ();
3747 string name = property.Name;
3748 foreach (IMemberReference mi in defs) {
3749 if (mi == property) {
3754 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
3756 if (property.Parameters.Count != 0) {
3757 AppendParameters (buf, method, property.Parameters, '[', ']');
3761 if (set_visible != null) {
3762 if (set_visible != visibility)
3763 buf.Append (' ').Append (set_visible);
3764 buf.Append (" set;");
3766 if (get_visible != null) {
3767 if (get_visible != visibility)
3768 buf.Append (' ').Append (get_visible);
3769 buf.Append (" get;");
3773 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
3776 protected override string GetFieldDeclaration (FieldDefinition field)
3778 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
3779 if (declType.IsEnum && field.Name == "value__")
3780 return null; // This member of enums aren't documented.
3782 StringBuilder buf = new StringBuilder ();
3783 AppendFieldVisibility (buf, field);
3784 if (buf.Length == 0)
3787 if (declType.IsEnum)
3790 if (field.IsStatic && !field.IsLiteral)
3791 buf.Append (" static");
3792 if (field.IsInitOnly)
3793 buf.Append (" readonly");
3794 if (field.IsLiteral)
3795 buf.Append (" const");
3797 buf.Append (' ').Append (GetName (field.FieldType)).Append (' ');
3798 buf.Append (field.Name);
3799 AppendFieldValue (buf, field);
3802 return buf.ToString ();
3805 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
3808 return buf.Append ("public");
3809 if (field.IsFamily || field.IsFamilyOrAssembly)
3810 return buf.Append ("protected");
3814 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
3816 // enums have a value__ field, which we ignore
3817 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
3818 field.DeclaringType.IsGenericType ())
3820 if (field.HasConstant && field.IsLiteral) {
3823 val = field.Constant;
3828 buf.Append (" = ").Append ("null");
3829 else if (val is Enum)
3830 buf.Append (" = ").Append (val.ToString ());
3831 else if (val is IFormattable) {
3832 string value = ((IFormattable)val).ToString();
3834 value = "\"" + value + "\"";
3835 buf.Append (" = ").Append (value);
3841 protected override string GetEventDeclaration (EventDefinition e)
3843 StringBuilder buf = new StringBuilder ();
3844 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
3848 AppendModifiers (buf, e.AddMethod);
3850 buf.Append (" event ");
3851 buf.Append (GetName (e.EventType)).Append (' ');
3852 buf.Append (e.Name).Append (';');
3854 return buf.ToString ();
3858 class CSharpMemberFormatter : CSharpFullMemberFormatter {
3859 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3865 class DocTypeFullMemberFormatter : MemberFormatter {
3866 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
3868 protected override char NestedTypeSeparator {
3873 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
3874 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3880 class SlashDocMemberFormatter : MemberFormatter {
3882 protected override char[] GenericTypeContainer {
3883 get {return new char[]{'{', '}'};}
3886 private bool AddTypeCount = true;
3888 private TypeReference genDeclType;
3889 private MethodReference genDeclMethod;
3891 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3893 if (type is GenericParameter) {
3895 if (genDeclType != null) {
3896 GenericParameterCollection genArgs = genDeclType.GenericParameters;
3897 for (int i = 0; i < genArgs.Count; ++i) {
3898 if (genArgs [i].Name == type.Name) {
3899 buf.Append ('`').Append (i);
3904 if (genDeclMethod != null) {
3905 GenericParameterCollection genArgs = null;
3906 if (genDeclMethod.IsGenericMethod ()) {
3907 genArgs = genDeclMethod.GenericParameters;
3908 for (int i = 0; i < genArgs.Count; ++i) {
3909 if (genArgs [i].Name == type.Name) {
3910 buf.Append ("``").Append (i);
3916 if (genDeclType == null && genDeclMethod == null) {
3917 // Probably from within an explicitly implemented interface member,
3918 // where CSC uses parameter names instead of indices (why?), e.g.
3919 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
3920 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
3921 buf.Append (type.Name);
3923 if (buf.Length == l) {
3924 throw new Exception (string.Format (
3925 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
3926 type.Name, genDeclType, genDeclMethod));
3930 base.AppendTypeName (buf, type);
3932 int numArgs = type.GenericParameters.Count;
3933 if (type.DeclaringType != null)
3934 numArgs -= type.GenericParameters.Count;
3936 buf.Append ('`').Append (numArgs);
3943 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3946 base.AppendGenericType (buf, type);
3948 AppendType (buf, type);
3952 private StringBuilder AppendType (StringBuilder buf, TypeReference type)
3954 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
3955 bool insertNested = false;
3956 int prevParamCount = 0;
3957 foreach (var decl in decls) {
3959 buf.Append (NestedTypeSeparator);
3960 insertNested = true;
3961 base.AppendTypeName (buf, decl);
3962 int argCount = DocUtils.GetGenericArgumentCount (decl);
3963 int numArgs = argCount - prevParamCount;
3964 prevParamCount = argCount;
3966 buf.Append ('`').Append (numArgs);
3971 public override string GetDeclaration (IMemberReference member)
3973 TypeReference r = member as TypeReference;
3975 return "T:" + GetTypeName (r);
3977 return base.GetDeclaration (member);
3980 protected override string GetConstructorName (MethodReference constructor)
3982 return GetMethodDefinitionName (constructor, "#ctor");
3985 protected override string GetMethodName (MethodReference method)
3988 MethodDefinition methodDef = method as MethodDefinition;
3989 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
3992 TypeReference iface;
3993 MethodReference ifaceMethod;
3994 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
3995 AddTypeCount = false;
3996 name = GetTypeName (iface) + "." + ifaceMethod.Name;
3997 AddTypeCount = true;
3999 return GetMethodDefinitionName (method, name);
4002 private string GetMethodDefinitionName (MethodReference method, string name)
4004 StringBuilder buf = new StringBuilder ();
4005 buf.Append (GetTypeName (method.DeclaringType));
4007 buf.Append (name.Replace (".", "#"));
4008 if (method.IsGenericMethod ()) {
4009 GenericParameterCollection genArgs = method.GenericParameters;
4010 if (genArgs.Count > 0)
4011 buf.Append ("``").Append (genArgs.Count);
4013 ParameterDefinitionCollection parameters = method.Parameters;
4015 genDeclType = method.DeclaringType;
4016 genDeclMethod = method;
4017 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
4021 genDeclMethod = null;
4023 return buf.ToString ();
4026 private StringBuilder AppendParameters (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinitionCollection parameters)
4028 if (parameters.Count == 0)
4033 AppendParameter (buf, genArgs, parameters [0]);
4034 for (int i = 1; i < parameters.Count; ++i) {
4036 AppendParameter (buf, genArgs, parameters [i]);
4039 return buf.Append (')');
4042 private StringBuilder AppendParameter (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinition parameter)
4044 AddTypeCount = false;
4045 buf.Append (GetTypeName (parameter.ParameterType));
4046 AddTypeCount = true;
4050 protected override string GetPropertyName (PropertyReference property)
4054 PropertyDefinition propertyDef = property as PropertyDefinition;
4055 MethodDefinition method = null;
4056 if (propertyDef != null)
4057 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
4058 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
4059 name = property.Name;
4061 TypeReference iface;
4062 MethodReference ifaceMethod;
4063 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4064 AddTypeCount = false;
4065 name = string.Join ("#", new string[]{
4066 GetTypeName (iface).Replace (".", "#"),
4067 DocUtils.GetMember (property.Name)
4069 AddTypeCount = true;
4072 StringBuilder buf = new StringBuilder ();
4073 buf.Append (GetName (property.DeclaringType));
4076 ParameterDefinitionCollection parameters = property.Parameters;
4077 if (parameters.Count > 0) {
4078 genDeclType = property.DeclaringType;
4080 GenericParameterCollection genArgs = property.DeclaringType.GenericParameters;
4081 AppendParameter (buf, genArgs, parameters [0]);
4082 for (int i = 1; i < parameters.Count; ++i) {
4084 AppendParameter (buf, genArgs, parameters [i]);
4089 return buf.ToString ();
4092 protected override string GetFieldName (FieldReference field)
4094 return string.Format ("{0}.{1}",
4095 GetName (field.DeclaringType), field.Name);
4098 protected override string GetEventName (EventReference e)
4100 return string.Format ("{0}.{1}",
4101 GetName (e.DeclaringType), e.Name);
4104 protected override string GetTypeDeclaration (TypeDefinition type)
4106 string name = GetName (type);
4112 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4114 string name = GetName (constructor);
4120 protected override string GetMethodDeclaration (MethodDefinition method)
4122 string name = GetName (method);
4125 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4126 genDeclType = method.DeclaringType;
4127 genDeclMethod = method;
4128 name += "~" + GetName (method.ReturnType.ReturnType);
4130 genDeclMethod = null;
4135 protected override string GetPropertyDeclaration (PropertyDefinition property)
4137 string name = GetName (property);
4143 protected override string GetFieldDeclaration (FieldDefinition field)
4145 string name = GetName (field);
4151 protected override string GetEventDeclaration (EventDefinition e)
4153 string name = GetName (e);
4160 class FileNameMemberFormatter : SlashDocMemberFormatter {
4161 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4166 protected override char NestedTypeSeparator {