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, true);
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, false);
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");
1533 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1535 MakeAttributes (me, p.CustomAttributes, false);
1536 MakeReturnValue(me, mi);
1537 if (mi is MethodReference) {
1538 MethodReference mb = (MethodReference) mi;
1539 if (mb.IsGenericMethod ())
1540 MakeTypeParameters (me, mb.GenericParameters);
1542 MakeParameters(me, mi);
1545 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1546 WriteElementText(me, "MemberValue", fieldValue);
1548 info.Node = WriteElement (me, "Docs");
1550 UpdateExtensionMethods (me, info);
1553 static readonly string[] ValidExtensionMembers = {
1562 static readonly string[] ValidExtensionDocMembers = {
1568 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1570 MethodDefinition me = info.Member as MethodDefinition;
1573 if (info.Parameters.Count < 1)
1575 if (!DocUtils.IsExtensionMethod (me))
1578 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1579 XmlNode member = e.CloneNode (true);
1580 em.AppendChild (member);
1581 RemoveExcept (member, ValidExtensionMembers);
1582 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1583 WriteElementText (member, "MemberType", "ExtensionMethod");
1584 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1585 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1586 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1587 member.AppendChild (link);
1588 AddTargets (em, info);
1590 extensionMethods.Add (em);
1593 private static void RemoveExcept (XmlNode node, string[] except)
1597 MyXmlNodeList remove = null;
1598 foreach (XmlNode n in node.ChildNodes) {
1599 if (Array.BinarySearch (except, n.Name) < 0) {
1601 remove = new MyXmlNodeList ();
1606 foreach (XmlNode n in remove)
1607 node.RemoveChild (n);
1610 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1612 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1613 member.PrependChild (targets);
1614 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1615 AppendElementAttributeText (targets, "Target", "Type",
1616 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1619 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1620 ConstraintCollection constraints = gp.Constraints;
1621 if (constraints.Count == 0)
1622 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1624 foreach (TypeReference c in constraints)
1625 AppendElementAttributeText(targets, "Target", "Type",
1626 slashdocFormatter.GetDeclaration (c));
1630 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1633 TypeDefinition type = field.DeclaringType.Resolve ();
1634 if (type != null && type.IsEnum) return false;
1636 if (type != null && type.IsGenericType ()) return false;
1637 if (!field.HasConstant)
1639 if (field.IsLiteral) {
1640 object val = field.Constant;
1641 if (val == null) value = "null";
1642 else if (val is Enum) value = val.ToString();
1643 else if (val is IFormattable) {
1644 value = ((IFormattable)val).ToString();
1646 value = "\"" + value + "\"";
1648 if (value != null && value != "")
1654 // XML HELPER FUNCTIONS
1656 private static XmlElement WriteElement(XmlNode parent, string element) {
1657 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1659 string[] path = element.Split('/');
1660 foreach (string p in path) {
1661 ret = (XmlElement)parent.SelectSingleNode(p);
1664 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1665 ename = ename.Substring(0, ename.IndexOf('['));
1666 ret = parent.OwnerDocument.CreateElement(ename);
1667 parent.AppendChild(ret);
1676 private static void WriteElementText(XmlNode parent, string element, string value) {
1677 XmlElement node = WriteElement(parent, element);
1678 node.InnerText = value;
1681 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1683 XmlElement n = parent.OwnerDocument.CreateElement (element);
1684 parent.AppendChild (n);
1685 n.InnerText = value;
1689 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1691 XmlElement n = parent.OwnerDocument.CreateElement (element);
1692 parent.AppendChild (n);
1693 n.SetAttribute (attribute, value);
1697 private static XmlNode CopyNode (XmlNode source, XmlNode dest)
1699 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1700 dest.AppendChild (copy);
1704 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1705 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1708 node = WriteElement(parent, element);
1709 node.InnerText = value;
1711 private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
1712 XmlElement node = WriteElement(parent, element);
1713 if (node.GetAttribute(attribute) == value) return;
1714 node.SetAttribute(attribute, value);
1716 private static void ClearElement(XmlElement parent, string name) {
1717 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1719 parent.RemoveChild(node);
1722 // DOCUMENTATION HELPER FUNCTIONS
1724 private void MakeDocNode (DocsNodeInfo info)
1726 List<GenericParameter> genericParams = info.GenericParameters;
1727 ParameterDefinitionCollection parameters = info.Parameters;
1728 TypeReference returntype = info.ReturnType;
1729 bool returnisreturn = info.ReturnIsReturn;
1730 XmlElement e = info.Node;
1731 bool addremarks = info.AddRemarks;
1733 WriteElementInitialText(e, "summary", "To be added.");
1735 if (parameters != null) {
1736 string[] values = new string [parameters.Count];
1737 for (int i = 0; i < values.Length; ++i)
1738 values [i] = parameters [i].Name;
1739 UpdateParameters (e, "param", values);
1742 if (genericParams != null) {
1743 string[] values = new string [genericParams.Count];
1744 for (int i = 0; i < values.Length; ++i)
1745 values [i] = genericParams [i].Name;
1746 UpdateParameters (e, "typeparam", values);
1749 string retnodename = null;
1750 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
1751 retnodename = returnisreturn ? "returns" : "value";
1752 string retnodename_other = !returnisreturn ? "returns" : "value";
1754 // If it has a returns node instead of a value node, change its name.
1755 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1756 if (retother != null) {
1757 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1758 foreach (XmlNode node in retother)
1759 retnode.AppendChild(node.CloneNode(true));
1760 e.ReplaceChild(retnode, retother);
1762 WriteElementInitialText(e, retnodename, "To be added.");
1765 ClearElement(e, "returns");
1766 ClearElement(e, "value");
1770 WriteElementInitialText(e, "remarks", "To be added.");
1772 if (exceptions.HasValue && info.Member != null &&
1773 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
1774 UpdateExceptions (e, info.Member);
1777 if (info.EcmaDocs != null) {
1778 XmlReader r = info.EcmaDocs;
1779 int depth = r.Depth;
1780 r.ReadStartElement ("Docs");
1782 if (r.Name == "Docs") {
1783 if (r.Depth == depth && r.NodeType == XmlNodeType.EndElement)
1786 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
1788 if (!r.IsStartElement ())
1793 string name = r.GetAttribute ("name");
1796 XmlNode doc = e.SelectSingleNode (
1797 r.Name + "[@name='" + name + "']");
1798 string value = r.ReadInnerXml ();
1800 doc.InnerXml = value.Replace ("\r", "");
1807 string name = r.Name;
1808 string cref = r.GetAttribute ("cref");
1811 XmlNode doc = e.SelectSingleNode (
1812 r.Name + "[@cref='" + cref + "']");
1813 string value = r.ReadInnerXml ().Replace ("\r", "");
1815 doc.InnerXml = value;
1817 XmlElement n = e.OwnerDocument.CreateElement (name);
1818 n.SetAttribute ("cref", cref);
1825 string name = r.Name;
1826 string xpath = r.Name;
1827 StringList attributes = new StringList (r.AttributeCount);
1828 if (r.MoveToFirstAttribute ()) {
1830 attributes.Add ("@" + r.Name + "=\"" + r.Value + "\"");
1831 } while (r.MoveToNextAttribute ());
1834 if (attributes.Count > 0) {
1835 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
1837 XmlNode doc = e.SelectSingleNode (xpath);
1838 string value = r.ReadInnerXml ().Replace ("\r", "");
1840 doc.InnerXml = value;
1843 XmlElement n = e.OwnerDocument.CreateElement (name);
1845 foreach (string a in attributes) {
1846 int eq = a.IndexOf ('=');
1847 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
1856 if (info.SlashDocs != null) {
1857 XmlNode elem = info.SlashDocs;
1859 if (elem.SelectSingleNode("summary") != null)
1860 ClearElement(e, "summary");
1861 if (elem.SelectSingleNode("remarks") != null)
1862 ClearElement(e, "remarks");
1863 if (elem.SelectSingleNode("value") != null)
1864 ClearElement(e, "value");
1865 if (retnodename != null && elem.SelectSingleNode(retnodename) != null)
1866 ClearElement(e, retnodename);
1868 foreach (XmlNode child in elem.ChildNodes) {
1869 switch (child.Name) {
1872 XmlAttribute name = child.Attributes ["name"];
1875 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
1877 p2.InnerXml = child.InnerXml;
1882 case "permission": {
1883 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
1886 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
1888 a = e.OwnerDocument.CreateElement (child.Name);
1889 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
1892 a.InnerXml = child.InnerXml;
1896 XmlAttribute cref = child.Attributes ["cref"];
1899 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
1901 a = e.OwnerDocument.CreateElement ("altmember");
1902 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
1908 CopyNode (child, e);
1915 OrderDocsNodes (e, e.ChildNodes);
1916 NormalizeWhitespace(e);
1919 static readonly string[] DocsNodeOrder = {
1920 "typeparam", "param", "summary", "returns", "value", "remarks",
1923 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
1925 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1926 for (int i = 0; i < DocsNodeOrder.Length; ++i) {
1927 for (int j = 0; j < children.Count; ++j) {
1928 XmlNode c = children [j];
1929 if (c.Name == DocsNodeOrder [i]) {
1930 newChildren.Add (c);
1934 if (newChildren.Count >= 0)
1935 docs.PrependChild ((XmlNode) newChildren [0]);
1936 for (int i = 1; i < newChildren.Count; ++i) {
1937 XmlNode prev = (XmlNode) newChildren [i-1];
1938 XmlNode cur = (XmlNode) newChildren [i];
1939 docs.RemoveChild (cur);
1940 docs.InsertAfter (cur, prev);
1945 private void UpdateParameters (XmlElement e, string element, string[] values)
1947 if (values != null) {
1948 XmlNode[] paramnodes = new XmlNode[values.Length];
1950 // Some documentation had param nodes with leading spaces.
1951 foreach (XmlElement paramnode in e.SelectNodes(element)){
1952 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
1955 // If a member has only one parameter, we can track changes to
1956 // the name of the parameter easily.
1957 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
1958 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
1961 bool reinsert = false;
1963 // Pick out existing and still-valid param nodes, and
1964 // create nodes for parameters not in the file.
1965 Hashtable seenParams = new Hashtable();
1966 for (int pi = 0; pi < values.Length; pi++) {
1967 string p = values [pi];
1970 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
1971 if (paramnodes[pi] != null) continue;
1973 XmlElement pe = e.OwnerDocument.CreateElement(element);
1974 pe.SetAttribute("name", p);
1975 pe.InnerText = "To be added.";
1976 paramnodes[pi] = pe;
1980 // Remove parameters that no longer exist and check all params are in the right order.
1982 MyXmlNodeList todelete = new MyXmlNodeList ();
1983 foreach (XmlElement paramnode in e.SelectNodes(element)) {
1984 string name = paramnode.GetAttribute("name");
1985 if (!seenParams.ContainsKey(name)) {
1986 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1987 Warning ("The following param node can only be deleted if the --delete option is given: ");
1988 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
1990 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
1991 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1995 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
1996 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1997 e.ParentNode.Attributes ["MemberName"].Value,
2000 Warning ("\tValue={0}", paramnode.OuterXml);
2002 todelete.Add (paramnode);
2007 if ((int)seenParams[name] != idx)
2013 foreach (XmlNode n in todelete) {
2014 n.ParentNode.RemoveChild (n);
2017 // Re-insert the parameter nodes at the top of the doc section.
2019 for (int pi = values.Length-1; pi >= 0; pi--)
2020 e.PrependChild(paramnodes[pi]);
2022 // Clear all existing param nodes
2023 foreach (XmlNode paramnode in e.SelectNodes(element)) {
2024 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2025 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
2026 Console.WriteLine(paramnode.OuterXml);
2028 paramnode.ParentNode.RemoveChild(paramnode);
2034 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
2036 string existingName = pe.GetAttribute ("name");
2037 pe.SetAttribute ("name", newName);
2038 if (existingName == newName)
2040 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
2041 if (paramref.GetAttribute ("name").Trim () == existingName)
2042 paramref.SetAttribute ("name", newName);
2045 class CrefComparer : XmlNodeComparer {
2047 public CrefComparer ()
2051 public override int Compare (XmlNode x, XmlNode y)
2053 string xType = x.Attributes ["cref"].Value;
2054 string yType = y.Attributes ["cref"].Value;
2055 string xNamespace = GetNamespace (xType);
2056 string yNamespace = GetNamespace (yType);
2058 int c = xNamespace.CompareTo (yNamespace);
2061 return xType.CompareTo (yType);
2064 static string GetNamespace (string type)
2066 int n = type.LastIndexOf ('.');
2068 return type.Substring (0, n);
2069 return string.Empty;
2073 private void UpdateExceptions (XmlNode docs, IMemberReference member)
2075 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
2076 string cref = slashdocFormatter.GetDeclaration (source.Exception);
2077 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
2080 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
2081 e.SetAttribute ("cref", cref);
2082 e.InnerXml = "To be added; from: <see cref=\"" +
2083 string.Join ("\" />, <see cref=\"",
2084 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
2087 docs.AppendChild (e);
2089 SortXmlNodes (docs, docs.SelectNodes ("exception"),
2090 new CrefComparer ());
2093 private static void NormalizeWhitespace(XmlElement e) {
2094 // Remove all text and whitespace nodes from the element so it
2095 // is outputted with nice indentation and no blank lines.
2096 ArrayList deleteNodes = new ArrayList();
2097 foreach (XmlNode n in e)
2098 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2100 foreach (XmlNode n in deleteNodes)
2101 n.ParentNode.RemoveChild(n);
2104 private static bool UpdateAssemblyVersions (XmlElement root, IMemberReference member, bool add)
2106 TypeDefinition type = member as TypeDefinition;
2108 type = member.DeclaringType as TypeDefinition;
2109 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
2112 private static string GetAssemblyVersion (AssemblyDefinition assembly)
2114 return assembly.Name.Version.ToString();
2117 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
2119 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
2121 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2122 root.AppendChild(e);
2124 MyXmlNodeList matches = new MyXmlNodeList (assemblyVersions.Length);
2125 foreach (XmlElement v in e.SelectNodes ("AssemblyVersion")) {
2126 foreach (string sv in assemblyVersions)
2127 if (v.InnerText == sv)
2130 // matches.Count > 0 && add: ignore -- already present
2131 if (matches.Count > 0 && !add) {
2132 foreach (XmlNode c in matches)
2135 else if (matches.Count == 0 && add) {
2136 foreach (string sv in assemblyVersions) {
2137 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2142 // matches.Count == 0 && !add: ignore -- already not present
2144 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2145 SortXmlNodes (e, avs, new VersionComparer ());
2147 return avs.Count != 0;
2150 // FIXME: get TypeReferences instead of string comparison?
2151 private static string[] IgnorableAttributes = {
2152 // Security related attributes
2153 "System.Reflection.AssemblyKeyFileAttribute",
2154 "System.Reflection.AssemblyDelaySignAttribute",
2155 // Present in @RefType
2156 "System.Runtime.InteropServices.OutAttribute",
2157 // For naming the indexer to use when not using indexers
2158 "System.Reflection.DefaultMemberAttribute",
2159 // for decimal constants
2160 "System.Runtime.CompilerServices.DecimalConstantAttribute",
2161 // compiler generated code
2162 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
2163 // more compiler generated code, e.g. iterator methods
2164 "System.Diagnostics.DebuggerHiddenAttribute",
2165 "System.Runtime.CompilerServices.FixedBufferAttribute",
2166 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
2167 // extension methods
2168 "System.Runtime.CompilerServices.ExtensionAttribute",
2171 private void MakeAttributes (XmlElement root, CustomAttributeCollection attributes, bool assemblyAttributes)
2173 if (attributes.Count == 0) {
2174 ClearElement(root, "Attributes");
2179 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2183 e = root.OwnerDocument.CreateElement("Attributes");
2185 foreach (CustomAttribute attribute in attributes.Cast<CustomAttribute> ()
2186 .OrderBy (ca => ca.Constructor.DeclaringType.FullName)) {
2187 if (!attribute.Resolve ()) {
2189 Warning ("warning: could not resolve type {0}.",
2190 attribute.Constructor.DeclaringType.FullName);
2192 TypeDefinition attrType = attribute.Constructor.DeclaringType as TypeDefinition;
2193 if (attrType != null && !IsPublic (attrType))
2195 if (slashdocFormatter.GetName (attribute.Constructor.DeclaringType) == null)
2198 if (Array.IndexOf (IgnorableAttributes, attribute.Constructor.DeclaringType.FullName) >= 0)
2203 StringList fields = new StringList ();
2205 ParameterDefinitionCollection parameters = attribute.Constructor.Parameters;
2206 for (int i = 0; i < attribute.ConstructorParameters.Count; ++i) {
2207 fields.Add (MakeAttributesValueString (
2208 attribute.ConstructorParameters [i],
2209 parameters [i].ParameterType));
2212 (from de in attribute.Fields.Cast<DictionaryEntry> ()
2213 select new { Type=attribute.GetFieldType (de.Key.ToString ()), Name=de.Key, Value=de.Value })
2215 (from de in attribute.Properties.Cast<DictionaryEntry> ()
2216 select new { Type=attribute.GetPropertyType (de.Key.ToString ()), Name=de.Key, Value=de.Value }))
2217 .OrderBy (v => v.Name);
2218 foreach (var d in namedArgs)
2219 fields.Add (string.Format ("{0}={1}", d.Name,
2220 MakeAttributesValueString (d.Value, d.Type)));
2222 string a2 = String.Join(", ", fields.ToArray ());
2223 if (a2 != "") a2 = "(" + a2 + ")";
2225 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2228 string name = attribute.Constructor.DeclaringType.FullName;
2229 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
2230 WriteElementText(ae, "AttributeName", name + a2);
2233 if (b && e.ParentNode == null)
2234 root.AppendChild(e);
2236 ClearElement(root, "Attributes");
2238 NormalizeWhitespace(e);
2241 private static string MakeAttributesValueString (object v, TypeReference valueType)
2245 if (valueType.FullName == "System.Type")
2246 return "typeof(" + v.ToString () + ")";
2247 if (valueType.FullName == "System.String")
2248 return "\"" + v.ToString () + "\"";
2250 return (bool)v ? "true" : "false";
2251 TypeDefinition valueDef = valueType.Resolve ();
2252 if (valueDef == null || !valueDef.IsEnum)
2253 return v.ToString ();
2254 string typename = GetDocTypeFullName (valueType);
2255 var values = GetEnumerationValues (valueDef);
2256 ulong c = Convert.ToUInt64 (v);
2257 if (values.ContainsKey (c))
2258 return typename + "." + values [c];
2259 if (valueDef.CustomAttributes.Cast<CustomAttribute> ()
2260 .Any (ca => ca.Constructor.DeclaringType.FullName == "System.FlagsAttribute")) {
2261 return string.Join (" | ",
2262 (from i in values.Keys
2264 select typename + "." + values [i])
2267 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
2270 private static Dictionary<ulong, string> GetEnumerationValues (TypeDefinition type)
2272 var values = new Dictionary<ulong, string> ();
2274 (from f in type.Fields.Cast<FieldDefinition> ()
2275 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
2277 values [Convert.ToUInt64 (f.Constant)] = f.Name;
2282 private void MakeParameters (XmlElement root, ParameterDefinitionCollection parameters)
2284 XmlElement e = WriteElement(root, "Parameters");
2286 foreach (ParameterDefinition p in parameters) {
2287 XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
2289 pe.SetAttribute("Name", p.Name);
2290 pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
2291 if (p.ParameterType is ReferenceType) {
2292 if (p.IsOut) pe.SetAttribute("RefType", "out");
2293 else pe.SetAttribute("RefType", "ref");
2295 MakeAttributes (pe, p.CustomAttributes, false);
2299 private void MakeTypeParameters (XmlElement root, GenericParameterCollection typeParams)
2301 if (typeParams == null || typeParams.Count == 0) {
2302 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2304 root.RemoveChild (f);
2307 XmlElement e = WriteElement(root, "TypeParameters");
2309 foreach (GenericParameter t in typeParams) {
2310 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2312 pe.SetAttribute("Name", t.Name);
2313 MakeAttributes (pe, t.CustomAttributes, false);
2314 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2315 ConstraintCollection constraints = t.Constraints;
2316 GenericParameterAttributes attrs = t.Attributes;
2317 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2325 ce = root.OwnerDocument.CreateElement ("Constraints");
2327 pe.AppendChild (ce);
2328 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2329 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2330 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2331 AppendElementText (ce, "ParameterAttribute", "Covariant");
2332 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2333 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2334 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2335 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2336 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2337 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2338 foreach (TypeReference c in constraints) {
2339 TypeDefinition cd = c.Resolve ();
2340 AppendElementText (ce,
2341 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2342 GetDocTypeFullName (c));
2347 private void MakeParameters (XmlElement root, IMemberReference mi)
2349 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2350 MakeParameters (root, ((MethodDefinition)mi).Parameters);
2351 else if (mi is MethodDefinition) {
2352 MethodDefinition mb = (MethodDefinition) mi;
2353 ParameterDefinitionCollection parameters = mb.Parameters;
2354 MakeParameters(root, parameters);
2355 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2356 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2357 p.SetAttribute ("RefType", "this");
2360 else if (mi is PropertyDefinition) {
2361 ParameterDefinitionCollection parameters = ((PropertyDefinition)mi).Parameters;
2362 if (parameters.Count > 0)
2363 MakeParameters(root, parameters);
2367 else if (mi is FieldDefinition) return;
2368 else if (mi is EventDefinition) return;
2369 else throw new ArgumentException();
2372 private static string GetDocParameterType (TypeReference type)
2374 return GetDocTypeFullName (type).Replace ("@", "&");
2377 private void MakeReturnValue (XmlElement root, TypeReference type, CustomAttributeCollection attributes)
2379 XmlElement e = WriteElement(root, "ReturnValue");
2381 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2382 if (attributes != null)
2383 MakeAttributes(e, attributes, false);
2386 private void MakeReturnValue (XmlElement root, IMemberReference mi)
2388 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2390 else if (mi is MethodDefinition)
2391 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType.ReturnType, ((MethodDefinition)mi).ReturnType.CustomAttributes);
2392 else if (mi is PropertyDefinition)
2393 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
2394 else if (mi is FieldDefinition)
2395 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
2396 else if (mi is EventDefinition)
2397 MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
2399 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2402 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2404 IMemberReference mi = info.Member;
2405 if (mi is TypeDefinition) return null;
2407 string sigs = MakeMemberSignature(mi);
2408 if (sigs == null) return null; // not publicly visible
2410 // no documentation for property/event accessors. Is there a better way of doing this?
2411 if (mi.Name.StartsWith("get_")) return null;
2412 if (mi.Name.StartsWith("set_")) return null;
2413 if (mi.Name.StartsWith("add_")) return null;
2414 if (mi.Name.StartsWith("remove_")) return null;
2415 if (mi.Name.StartsWith("raise_")) return null;
2417 XmlElement me = doc.CreateElement("Member");
2418 me.SetAttribute("MemberName", GetMemberName (mi));
2422 if (exceptions.HasValue &&
2423 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2424 UpdateExceptions (info.Node, info.Member);
2426 if (since != null) {
2427 XmlNode docs = me.SelectSingleNode("Docs");
2428 docs.AppendChild (CreateSinceNode (doc));
2434 private static string GetMemberName (IMemberReference mi)
2436 MethodDefinition mb = mi as MethodDefinition;
2438 PropertyDefinition pi = mi as PropertyDefinition;
2441 return DocUtils.GetPropertyName (pi);
2443 StringBuilder sb = new StringBuilder (mi.Name.Length);
2444 if (!DocUtils.IsExplicitlyImplemented (mb))
2445 sb.Append (mi.Name);
2447 TypeReference iface;
2448 MethodReference ifaceMethod;
2449 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2450 sb.Append (GetDocTypeFullName (iface));
2452 sb.Append (ifaceMethod.Name);
2454 if (mb.IsGenericMethod ()) {
2455 GenericParameterCollection typeParams = mb.GenericParameters;
2456 if (typeParams.Count > 0) {
2458 sb.Append (typeParams [0].Name);
2459 for (int i = 1; i < typeParams.Count; ++i)
2460 sb.Append (",").Append (typeParams [i].Name);
2464 return sb.ToString ();
2467 private static int CountChars (string s, char c)
2470 for (int i = 0; i < s.Length; ++i) {
2477 /// SIGNATURE GENERATION FUNCTIONS
2479 static string MakeTypeSignature (TypeReference type)
2481 return csharpFormatter.GetDeclaration (type);
2484 static string MakeMemberSignature (IMemberReference mi)
2486 return csharpFullFormatter.GetDeclaration (mi);
2489 static string GetMemberType (IMemberReference mi)
2491 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2492 return "Constructor";
2493 if (mi is MethodDefinition)
2495 if (mi is PropertyDefinition)
2497 if (mi is FieldDefinition)
2499 if (mi is EventDefinition)
2501 throw new ArgumentException();
2504 private static string GetDocTypeName (TypeReference type)
2506 return docTypeFormatter.GetName (type);
2509 private static string GetDocTypeFullName (TypeReference type)
2511 return DocTypeFullMemberFormatter.Default.GetName (type);
2514 class DocsNodeInfo {
2515 public DocsNodeInfo (XmlElement node)
2520 public DocsNodeInfo (XmlElement node, TypeDefinition type)
2526 public DocsNodeInfo (XmlElement node, IMemberReference member)
2529 SetMemberInfo (member);
2532 void SetType (TypeDefinition type)
2535 throw new ArgumentNullException ("type");
2536 GenericParameters = new List<GenericParameter> (type.GenericParameters.Cast<GenericParameter> ());
2537 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
2538 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
2539 for (int i = 0; i < declTypes.Count - 1; ++i) {
2540 int remove = System.Math.Min (maxGenArgs,
2541 DocUtils.GetGenericArgumentCount (declTypes [i]));
2542 maxGenArgs -= remove;
2543 while (remove-- > 0)
2544 GenericParameters.RemoveAt (0);
2546 if (DocUtils.IsDelegate (type)) {
2547 Parameters = type.GetMethod("Invoke").Parameters;
2548 ReturnType = type.GetMethod("Invoke").ReturnType.ReturnType;
2550 SetSlashDocs (type);
2553 void SetMemberInfo (IMemberReference member)
2556 throw new ArgumentNullException ("member");
2557 ReturnIsReturn = true;
2561 if (member is MethodReference ) {
2562 MethodReference mr = (MethodReference) member;
2563 Parameters = mr.Parameters;
2564 if (mr.IsGenericMethod ()) {
2565 GenericParameters = new List<GenericParameter> (mr.GenericParameters.Cast<GenericParameter> ());
2568 else if (member is PropertyDefinition) {
2569 Parameters = ((PropertyDefinition) member).Parameters;
2572 if (member is MethodDefinition) {
2573 ReturnType = ((MethodDefinition) member).ReturnType.ReturnType;
2574 } else if (member is PropertyDefinition) {
2575 ReturnType = ((PropertyDefinition) member).PropertyType;
2576 ReturnIsReturn = false;
2579 // no remarks section for enum members
2580 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
2582 SetSlashDocs (member);
2585 private void SetSlashDocs (IMemberReference member)
2587 if (slashdocs == null)
2590 string slashdocsig = slashdocFormatter.GetDeclaration (member);
2591 if (slashdocsig != null)
2592 SlashDocs = slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
2595 public TypeReference ReturnType;
2596 public List<GenericParameter> GenericParameters;
2597 public ParameterDefinitionCollection Parameters;
2598 public bool ReturnIsReturn;
2599 public XmlElement Node;
2600 public bool AddRemarks = true;
2601 public XmlNode SlashDocs;
2602 public XmlReader EcmaDocs;
2603 public IMemberReference Member;
2606 static string GetXPathForMember (DocumentationMember member)
2608 StringBuilder xpath = new StringBuilder ();
2609 xpath.Append ("//Members/Member[@MemberName=\"")
2610 .Append (member.MemberName)
2612 if (member.Parameters != null && member.Parameters.Count > 0) {
2613 xpath.Append ("/Parameters[count(Parameter) = ")
2614 .Append (member.Parameters.Count);
2615 for (int i = 0; i < member.Parameters.Count; ++i) {
2616 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2617 xpath.Append (member.Parameters [i]);
2618 xpath.Append ("\"");
2620 xpath.Append ("]/..");
2622 return xpath.ToString ();
2625 public static string GetXPathForMember (XPathNavigator member)
2627 StringBuilder xpath = new StringBuilder ();
2628 xpath.Append ("//Type[@FullName=\"")
2629 .Append (member.SelectSingleNode ("../../@FullName").Value)
2631 xpath.Append ("Members/Member[@MemberName=\"")
2632 .Append (member.SelectSingleNode ("@MemberName").Value)
2634 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2635 if (parameters.Count > 0) {
2636 xpath.Append ("/Parameters[count(Parameter) = ")
2637 .Append (parameters.Count);
2639 while (parameters.MoveNext ()) {
2641 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2642 xpath.Append (parameters.Current.Value);
2643 xpath.Append ("\"");
2645 xpath.Append ("]/..");
2647 return xpath.ToString ();
2650 public static string GetXPathForMember (IMemberReference member)
2652 StringBuilder xpath = new StringBuilder ();
2653 xpath.Append ("//Type[@FullName=\"")
2654 .Append (member.DeclaringType.FullName)
2656 xpath.Append ("Members/Member[@MemberName=\"")
2657 .Append (GetMemberName (member))
2660 ParameterDefinitionCollection parameters = null;
2661 if (member is MethodDefinition)
2662 parameters = ((MethodDefinition) member).Parameters;
2663 else if (member is PropertyDefinition) {
2664 parameters = ((PropertyDefinition) member).Parameters;
2666 if (parameters != null && parameters.Count > 0) {
2667 xpath.Append ("/Parameters[count(Parameter) = ")
2668 .Append (parameters.Count);
2669 for (int i = 0; i < parameters.Count; ++i) {
2670 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2671 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2672 xpath.Append ("\"");
2674 xpath.Append ("]/..");
2676 return xpath.ToString ();
2680 static class CecilExtensions {
2681 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type)
2683 foreach (var c in type.Constructors)
2684 yield return (IMemberReference) c;
2685 foreach (var e in type.Events)
2686 yield return (IMemberReference) e;
2687 foreach (var f in type.Fields)
2688 yield return (IMemberReference) f;
2689 foreach (var m in type.Methods)
2690 yield return (IMemberReference) m;
2691 foreach (var t in type.NestedTypes)
2692 yield return (IMemberReference) t;
2693 foreach (var p in type.Properties)
2694 yield return (IMemberReference) p;
2697 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type, string member)
2699 return GetMembers (type).Where (m => m.Name == member);
2702 public static IMemberReference GetMember (this TypeDefinition type, string member)
2704 return GetMembers (type, member).EnsureZeroOrOne ();
2707 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2709 if (source.Count () > 1)
2710 throw new InvalidOperationException ("too many matches");
2711 return source.FirstOrDefault ();
2714 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2716 return type.Methods.Cast<MethodDefinition> ()
2717 .Where (m => m.Name == method)
2718 .EnsureZeroOrOne ();
2721 public static IEnumerable<IMemberReference> GetDefaultMembers (this TypeReference type)
2723 TypeDefinition def = type as TypeDefinition;
2725 return new IMemberReference [0];
2726 CustomAttribute defMemberAttr = type.CustomAttributes.Cast<CustomAttribute> ()
2727 .Where (c => c.Constructor.DeclaringType.FullName == "System.Reflection.DefaultMemberAttribute")
2729 if (defMemberAttr == null)
2730 return new IMemberReference [0];
2731 string name = (string) defMemberAttr.ConstructorParameters [0];
2732 return def.Properties.Cast<PropertyDefinition> ()
2733 .Where (p => p.Name == name)
2734 .Select (p => (IMemberReference) p);
2737 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2739 return assembly.Modules.Cast<ModuleDefinition> ()
2740 .SelectMany (md => md.Types.Cast<TypeDefinition> ());
2743 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2745 return GetTypes (assembly)
2746 .Where (td => td.FullName == type)
2747 .EnsureZeroOrOne ();
2750 public static bool IsGenericType (this TypeReference type)
2752 return type.GenericParameters.Count > 0;
2755 public static bool IsGenericMethod (this MethodReference method)
2757 return method.GenericParameters.Count > 0;
2760 public static IMemberReference Resolve (this IMemberReference member)
2762 EventReference er = member as EventReference;
2764 return er.Resolve ();
2765 FieldReference fr = member as FieldReference;
2767 return fr.Resolve ();
2768 MethodReference mr = member as MethodReference;
2770 return mr.Resolve ();
2771 PropertyReference pr = member as PropertyReference;
2773 return pr.Resolve ();
2774 TypeReference tr = member as TypeReference;
2776 return tr.Resolve ();
2777 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2781 static class DocUtils {
2782 public static bool IsExplicitlyImplemented (MethodDefinition method)
2784 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2787 public static string GetTypeDotMember (string name)
2789 int startType, startMethod;
2790 startType = startMethod = -1;
2791 for (int i = 0; i < name.Length; ++i) {
2792 if (name [i] == '.') {
2793 startType = startMethod;
2797 return name.Substring (startType+1);
2800 public static string GetMember (string name)
2802 int i = name.LastIndexOf ('.');
2805 return name.Substring (i+1);
2808 public static void GetInfoForExplicitlyImplementedMethod (
2809 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2813 if (method.Overrides.Count != 1)
2814 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2815 iface = method.Overrides [0].DeclaringType;
2816 ifaceMethod = method.Overrides [0];
2819 public static string GetPropertyName (PropertyDefinition pi)
2821 // Issue: (g)mcs-generated assemblies that explicitly implement
2822 // properties don't specify the full namespace, just the
2823 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2824 MethodDefinition method = pi.GetMethod;
2826 method = pi.SetMethod;
2827 if (!IsExplicitlyImplemented (method))
2830 // Need to determine appropriate namespace for this member.
2831 TypeReference iface;
2832 MethodReference ifaceMethod;
2833 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2834 return string.Join (".", new string[]{
2835 DocTypeFullMemberFormatter.Default.GetName (iface),
2836 GetMember (pi.Name)});
2839 public static string GetNamespace (TypeReference type)
2841 if (type.GetOriginalType ().IsNested)
2842 type = type.GetOriginalType ();
2843 while (type != null && type.IsNested)
2844 type = type.DeclaringType;
2846 return string.Empty;
2847 return type.Namespace;
2850 public static string PathCombine (string dir, string path)
2856 return Path.Combine (dir, path);
2859 public static bool IsExtensionMethod (MethodDefinition method)
2862 method.CustomAttributes.Cast<CustomAttribute> ()
2863 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2865 method.DeclaringType.CustomAttributes.Cast<CustomAttribute> ()
2866 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2870 public static bool IsDelegate (TypeDefinition type)
2872 TypeReference baseRef = type.BaseType;
2873 if (baseRef == null)
2875 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2876 baseRef.FullName == "System.MulticastDelegate";
2879 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2881 List<TypeReference> decls = new List<TypeReference> ();
2883 while (type.DeclaringType != null) {
2884 decls.Add (type.DeclaringType);
2885 type = type.DeclaringType;
2891 public static int GetGenericArgumentCount (TypeReference type)
2893 GenericInstanceType inst = type as GenericInstanceType;
2895 ? inst.GenericArguments.Count
2896 : type.GenericParameters.Count;
2899 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
2901 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
2902 List<TypeReference> userInterfaces = new List<TypeReference> ();
2903 foreach (TypeReference iface in type.Interfaces) {
2904 TypeReference lookup = iface.Resolve () ?? iface;
2905 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
2906 userInterfaces.Add (iface);
2908 return userInterfaces;
2911 private static string GetQualifiedTypeName (TypeReference type)
2913 return "[" + type.Scope.Name + "]" + type.FullName;
2916 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
2918 HashSet<string> inheritedInterfaces = new HashSet<string> ();
2919 Action<TypeDefinition> a = null;
2921 if (t == null) return;
2922 foreach (TypeReference r in t.Interfaces) {
2923 inheritedInterfaces.Add (GetQualifiedTypeName (r));
2927 TypeReference baseRef = type.BaseType;
2928 while (baseRef != null) {
2929 TypeDefinition baseDef = baseRef.Resolve ();
2930 if (baseDef != null) {
2932 baseRef = baseDef.BaseType;
2937 foreach (TypeReference r in type.Interfaces)
2939 return inheritedInterfaces;
2943 class DocumentationMember {
2944 public StringToStringMap MemberSignatures = new StringToStringMap ();
2945 public string ReturnType;
2946 public StringList Parameters;
2947 public string MemberName;
2948 public string MemberType;
2950 public DocumentationMember (XmlReader reader)
2952 MemberName = reader.GetAttribute ("MemberName");
2953 int depth = reader.Depth;
2955 StringList p = new StringList ();
2957 if (reader.NodeType != XmlNodeType.Element)
2959 switch (reader.Name) {
2960 case "MemberSignature":
2961 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
2964 MemberType = reader.ReadElementString ();
2967 if (reader.Depth == depth + 2)
2968 ReturnType = reader.ReadElementString ();
2971 if (reader.Depth == depth + 2)
2972 p.Add (reader.GetAttribute ("Type"));
2975 if (reader.Depth == depth + 1)
2979 } while (go && reader.Read () && reader.Depth >= depth);
2985 public DocumentationMember (XmlNode node)
2987 MemberName = node.Attributes ["MemberName"].Value;
2988 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
2989 XmlAttribute l = n.Attributes ["Language"];
2990 XmlAttribute v = n.Attributes ["Value"];
2991 if (l != null && v != null)
2992 MemberSignatures [l.Value] = v.Value;
2994 MemberType = node.SelectSingleNode ("MemberType").InnerText;
2995 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
2997 ReturnType = rt.InnerText;
2998 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
3000 Parameters = new StringList (p.Count);
3001 for (int i = 0; i < p.Count; ++i)
3002 Parameters.Add (p [i].Attributes ["Type"].Value);
3007 public enum MemberFormatterState {
3010 WithinGenericTypeContainer,
3013 public abstract class MemberFormatter {
3014 public virtual string GetName (IMemberReference member)
3016 TypeReference type = member as TypeReference;
3018 return GetTypeName (type);
3019 MethodReference method = member as MethodReference;
3020 if (method != null && method.Name == ".ctor") // method.IsConstructor
3021 return GetConstructorName (method);
3023 return GetMethodName (method);
3024 PropertyReference prop = member as PropertyReference;
3026 return GetPropertyName (prop);
3027 FieldReference field = member as FieldReference;
3029 return GetFieldName (field);
3030 EventReference e = member as EventReference;
3032 return GetEventName (e);
3033 throw new NotSupportedException ("Can't handle: " +
3034 (member == null ? "null" : member.GetType().ToString()));
3037 protected virtual string GetTypeName (TypeReference type)
3040 throw new ArgumentNullException ("type");
3041 return _AppendTypeName (new StringBuilder (type.Name.Length), type).ToString ();
3044 protected virtual char[] ArrayDelimeters {
3045 get {return new char[]{'[', ']'};}
3048 protected virtual MemberFormatterState MemberFormatterState { get; set; }
3050 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type)
3052 if (type is ArrayType) {
3053 TypeSpecification spec = type as TypeSpecification;
3054 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3055 .Append (ArrayDelimeters [0]);
3056 var origState = MemberFormatterState;
3057 MemberFormatterState = MemberFormatterState.WithinArray;
3058 ArrayType array = (ArrayType) type;
3059 int rank = array.Rank;
3061 buf.Append (new string (',', rank-1));
3062 MemberFormatterState = origState;
3063 return buf.Append (ArrayDelimeters [1]);
3065 if (type is ReferenceType) {
3066 return AppendRefTypeName (buf, type);
3068 if (type is PointerType) {
3069 return AppendPointerTypeName (buf, type);
3071 AppendNamespace (buf, type);
3072 if (type is GenericParameter) {
3073 return AppendTypeName (buf, type);
3075 GenericInstanceType genInst = type as GenericInstanceType;
3076 if (type.GenericParameters.Count == 0 &&
3077 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3078 return AppendFullTypeName (buf, type);
3080 return AppendGenericType (buf, type);
3083 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3085 string ns = DocUtils.GetNamespace (type);
3086 if (ns != null && ns.Length > 0)
3087 buf.Append (ns).Append ('.');
3091 private StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type)
3093 if (type.DeclaringType != null)
3094 AppendFullTypeName (buf, type.DeclaringType).Append (NestedTypeSeparator);
3095 return AppendTypeName (buf, type);
3098 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3100 return AppendTypeName (buf, type.Name);
3103 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3105 int n = typename.IndexOf ("`");
3107 return buf.Append (typename.Substring (0, n));
3108 return buf.Append (typename);
3111 protected virtual string RefTypeModifier {
3115 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type)
3117 TypeSpecification spec = type as TypeSpecification;
3118 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3119 .Append (RefTypeModifier);
3122 protected virtual string PointerModifier {
3126 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type)
3128 TypeSpecification spec = type as TypeSpecification;
3129 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3130 .Append (PointerModifier);
3133 protected virtual char[] GenericTypeContainer {
3134 get {return new char[]{'<', '>'};}
3137 protected virtual char NestedTypeSeparator {
3141 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3143 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3144 type is GenericInstanceType ? type.GetOriginalType () : type);
3145 List<TypeReference> genArgs = GetGenericArguments (type);
3148 bool insertNested = false;
3149 foreach (var decl in decls) {
3150 TypeReference declDef = decl.Resolve () ?? decl;
3152 buf.Append (NestedTypeSeparator);
3154 insertNested = true;
3155 AppendTypeName (buf, declDef);
3156 int ac = DocUtils.GetGenericArgumentCount (declDef);
3160 buf.Append (GenericTypeContainer [0]);
3161 var origState = MemberFormatterState;
3162 MemberFormatterState = MemberFormatterState.WithinGenericTypeContainer;
3163 _AppendTypeName (buf, genArgs [argIdx++]);
3164 for (int i = 1; i < c; ++i)
3165 _AppendTypeName (buf.Append (","), genArgs [argIdx++]);
3166 MemberFormatterState = origState;
3167 buf.Append (GenericTypeContainer [1]);
3173 private List<TypeReference> GetGenericArguments (TypeReference type)
3175 var args = new List<TypeReference> ();
3176 GenericInstanceType inst = type as GenericInstanceType;
3178 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
3180 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
3184 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3189 protected virtual string GetConstructorName (MethodReference constructor)
3191 return constructor.Name;
3194 protected virtual string GetMethodName (MethodReference method)
3199 protected virtual string GetPropertyName (PropertyReference property)
3201 return property.Name;
3204 protected virtual string GetFieldName (FieldReference field)
3209 protected virtual string GetEventName (EventReference e)
3214 public virtual string GetDeclaration (IMemberReference member)
3217 throw new ArgumentNullException ("member");
3218 TypeDefinition type = member as TypeDefinition;
3220 return GetTypeDeclaration (type);
3221 MethodDefinition method = member as MethodDefinition;
3222 if (method != null && method.IsConstructor)
3223 return GetConstructorDeclaration (method);
3225 return GetMethodDeclaration (method);
3226 PropertyDefinition prop = member as PropertyDefinition;
3228 return GetPropertyDeclaration (prop);
3229 FieldDefinition field = member as FieldDefinition;
3231 return GetFieldDeclaration (field);
3232 EventDefinition e = member as EventDefinition;
3234 return GetEventDeclaration (e);
3235 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3238 protected virtual string GetTypeDeclaration (TypeDefinition type)
3241 throw new ArgumentNullException ("type");
3242 StringBuilder buf = new StringBuilder (type.Name.Length);
3243 _AppendTypeName (buf, type);
3244 AppendGenericTypeConstraints (buf, type);
3245 return buf.ToString ();
3248 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
3250 return GetConstructorName (constructor);
3253 protected virtual string GetMethodDeclaration (MethodDefinition method)
3255 // Special signature for destructors.
3256 if (method.Name == "Finalize" && method.Parameters.Count == 0)
3257 return GetFinalizerName (method);
3259 StringBuilder buf = new StringBuilder ();
3261 AppendVisibility (buf, method);
3262 if (buf.Length == 0 &&
3263 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3266 AppendModifiers (buf, method);
3268 if (buf.Length != 0)
3270 buf.Append (GetName (method.ReturnType.ReturnType)).Append (" ");
3272 AppendMethodName (buf, method);
3273 AppendGenericMethod (buf, method).Append (" ");
3274 AppendParameters (buf, method, method.Parameters);
3275 AppendGenericMethodConstraints (buf, method);
3276 return buf.ToString ();
3279 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3281 return buf.Append (method.Name);
3284 protected virtual string GetFinalizerName (MethodDefinition method)
3289 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3294 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3299 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3304 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3309 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3314 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
3316 return GetPropertyName (property);
3319 protected virtual string GetFieldDeclaration (FieldDefinition field)
3321 return GetFieldName (field);
3324 protected virtual string GetEventDeclaration (EventDefinition e)
3326 return GetEventName (e);
3330 class CSharpFullMemberFormatter : MemberFormatter {
3332 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3334 string ns = DocUtils.GetNamespace (type);
3335 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
3336 buf.Append (ns).Append ('.');
3340 private string GetCSharpType (string t)
3343 case "System.Byte": return "byte";
3344 case "System.SByte": return "sbyte";
3345 case "System.Int16": return "short";
3346 case "System.Int32": return "int";
3347 case "System.Int64": return "long";
3349 case "System.UInt16": return "ushort";
3350 case "System.UInt32": return "uint";
3351 case "System.UInt64": return "ulong";
3353 case "System.Single": return "float";
3354 case "System.Double": return "double";
3355 case "System.Decimal": return "decimal";
3356 case "System.Boolean": return "bool";
3357 case "System.Char": return "char";
3358 case "System.Void": return "void";
3359 case "System.String": return "string";
3360 case "System.Object": return "object";
3365 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3367 if (type is GenericParameter)
3368 return AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
3369 string t = type.FullName;
3370 if (!t.StartsWith ("System.")) {
3371 return base.AppendTypeName (buf, type);
3374 string s = GetCSharpType (t);
3376 return buf.Append (s);
3378 return base.AppendTypeName (buf, type);
3381 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
3383 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeContainer)
3385 GenericParameterAttributes attrs = type.Attributes;
3386 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
3387 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
3391 buf.Append ("out ");
3395 protected override string GetTypeDeclaration (TypeDefinition type)
3397 string visibility = GetTypeVisibility (type.Attributes);
3398 if (visibility == null)
3401 StringBuilder buf = new StringBuilder ();
3403 buf.Append (visibility);
3406 MemberFormatter full = new CSharpFullMemberFormatter ();
3408 if (DocUtils.IsDelegate (type)) {
3409 buf.Append("delegate ");
3410 MethodDefinition invoke = type.GetMethod ("Invoke");
3411 buf.Append (full.GetName (invoke.ReturnType.ReturnType)).Append (" ");
3412 buf.Append (GetName (type));
3413 AppendParameters (buf, invoke, invoke.Parameters);
3414 AppendGenericTypeConstraints (buf, type);
3417 return buf.ToString();
3420 if (type.IsAbstract && !type.IsInterface)
3421 buf.Append("abstract ");
3422 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
3423 buf.Append("sealed ");
3424 buf.Replace ("abstract sealed", "static");
3426 buf.Append (GetTypeKind (type));
3428 buf.Append (GetCSharpType (type.FullName) == null
3433 TypeReference basetype = type.BaseType;
3434 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
3437 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
3438 .Select (iface => full.GetName (iface))
3442 if (basetype != null || interface_names.Count > 0)
3445 if (basetype != null) {
3446 buf.Append (full.GetName (basetype));
3447 if (interface_names.Count > 0)
3451 for (int i = 0; i < interface_names.Count; i++){
3454 buf.Append (interface_names [i]);
3456 AppendGenericTypeConstraints (buf, type);
3459 return buf.ToString ();
3462 static string GetTypeKind (TypeDefinition t)
3468 if (t.IsClass || t.FullName == "System.Enum")
3472 throw new ArgumentException(t.FullName);
3475 static string GetTypeVisibility (TypeAttributes ta)
3477 switch (ta & TypeAttributes.VisibilityMask) {
3478 case TypeAttributes.Public:
3479 case TypeAttributes.NestedPublic:
3482 case TypeAttributes.NestedFamily:
3483 case TypeAttributes.NestedFamORAssem:
3491 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3493 if (type.GenericParameters.Count == 0)
3495 return AppendConstraints (buf, type.GenericParameters);
3498 private StringBuilder AppendConstraints (StringBuilder buf, GenericParameterCollection genArgs)
3500 foreach (GenericParameter genArg in genArgs) {
3501 GenericParameterAttributes attrs = genArg.Attributes;
3502 ConstraintCollection constraints = genArg.Constraints;
3503 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
3506 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
3507 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
3508 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
3511 if (!isref && !isvt && !isnew && constraints.Count == 0)
3513 buf.Append (" where ").Append (genArg.Name).Append (" : ");
3515 buf.Append ("class");
3519 buf.Append ("struct");
3522 if (constraints.Count > 0 && !isvt) {
3525 buf.Append (GetTypeName (constraints [0]));
3526 for (int i = 1; i < constraints.Count; ++i)
3527 buf.Append (", ").Append (GetTypeName (constraints [i]));
3529 if (isnew && !isvt) {
3532 buf.Append ("new()");
3538 protected override string GetConstructorDeclaration (MethodDefinition constructor)
3540 StringBuilder buf = new StringBuilder ();
3541 AppendVisibility (buf, constructor);
3542 if (buf.Length == 0)
3546 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
3547 AppendParameters (buf, constructor, constructor.Parameters);
3550 return buf.ToString ();
3553 protected override string GetMethodDeclaration (MethodDefinition method)
3555 string decl = base.GetMethodDeclaration (method);
3561 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3563 if (DocUtils.IsExplicitlyImplemented (method)) {
3564 TypeReference iface;
3565 MethodReference ifaceMethod;
3566 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3567 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3569 .Append (ifaceMethod.Name);
3571 return base.AppendMethodName (buf, method);
3574 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3576 if (method.GenericParameters.Count == 0)
3578 return AppendConstraints (buf, method.GenericParameters);
3581 protected override string RefTypeModifier {
3585 protected override string GetFinalizerName (MethodDefinition method)
3587 return "~" + method.DeclaringType.Name + " ()";
3590 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3594 if (method.IsPublic)
3595 return buf.Append ("public");
3596 if (method.IsFamily || method.IsFamilyOrAssembly)
3597 return buf.Append ("protected");
3601 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3603 string modifiers = String.Empty;
3604 if (method.IsStatic) modifiers += " static";
3605 if (method.IsVirtual && !method.IsAbstract) {
3606 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3607 else modifiers += " override";
3609 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
3610 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
3611 if (method.IsFinal) modifiers += " sealed";
3612 if (modifiers == " virtual sealed") modifiers = "";
3614 return buf.Append (modifiers);
3617 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3619 if (method.IsGenericMethod ()) {
3620 GenericParameterCollection args = method.GenericParameters;
3621 if (args.Count > 0) {
3623 buf.Append (args [0].Name);
3624 for (int i = 1; i < args.Count; ++i)
3625 buf.Append (",").Append (args [i].Name);
3632 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3634 return AppendParameters (buf, method, parameters, '(', ')');
3637 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters, char begin, char end)
3641 if (parameters.Count > 0) {
3642 if (DocUtils.IsExtensionMethod (method))
3643 buf.Append ("this ");
3644 AppendParameter (buf, parameters [0]);
3645 for (int i = 1; i < parameters.Count; ++i) {
3647 AppendParameter (buf, parameters [i]);
3651 return buf.Append (end);
3654 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
3656 if (parameter.ParameterType is ReferenceType) {
3657 if (parameter.IsOut)
3658 buf.Append ("out ");
3660 buf.Append ("ref ");
3662 buf.Append (GetName (parameter.ParameterType)).Append (" ");
3663 return buf.Append (parameter.Name);
3666 protected override string GetPropertyDeclaration (PropertyDefinition property)
3668 MethodDefinition method;
3670 string get_visible = null;
3671 if ((method = property.GetMethod) != null &&
3672 (DocUtils.IsExplicitlyImplemented (method) ||
3673 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3674 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3675 string set_visible = null;
3676 if ((method = property.SetMethod) != null &&
3677 (DocUtils.IsExplicitlyImplemented (method) ||
3678 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3679 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3681 if ((set_visible == null) && (get_visible == null))
3685 StringBuilder buf = new StringBuilder ();
3686 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
3687 buf.Append (visibility = get_visible);
3688 else if (set_visible != null && get_visible == null)
3689 buf.Append (visibility = set_visible);
3691 buf.Append (visibility = "public");
3693 // Pick an accessor to use for static/virtual/override/etc. checks.
3694 method = property.SetMethod;
3696 method = property.GetMethod;
3698 string modifiers = String.Empty;
3699 if (method.IsStatic) modifiers += " static";
3700 if (method.IsVirtual && !method.IsAbstract) {
3701 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
3702 modifiers += " virtual";
3704 modifiers += " override";
3706 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
3707 if (method.IsAbstract && !declDef.IsInterface)
3708 modifiers += " abstract";
3710 modifiers += " sealed";
3711 if (modifiers == " virtual sealed")
3713 buf.Append (modifiers).Append (' ');
3715 buf.Append (GetName (property.PropertyType)).Append (' ');
3717 IEnumerable<IMemberReference> defs = property.DeclaringType.GetDefaultMembers ();
3718 string name = property.Name;
3719 foreach (IMemberReference mi in defs) {
3720 if (mi == property) {
3725 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
3727 if (property.Parameters.Count != 0) {
3728 AppendParameters (buf, method, property.Parameters, '[', ']');
3732 if (set_visible != null) {
3733 if (set_visible != visibility)
3734 buf.Append (' ').Append (set_visible);
3735 buf.Append (" set;");
3737 if (get_visible != null) {
3738 if (get_visible != visibility)
3739 buf.Append (' ').Append (get_visible);
3740 buf.Append (" get;");
3744 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
3747 protected override string GetFieldDeclaration (FieldDefinition field)
3749 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
3750 if (declType.IsEnum && field.Name == "value__")
3751 return null; // This member of enums aren't documented.
3753 StringBuilder buf = new StringBuilder ();
3754 AppendFieldVisibility (buf, field);
3755 if (buf.Length == 0)
3758 if (declType.IsEnum)
3761 if (field.IsStatic && !field.IsLiteral)
3762 buf.Append (" static");
3763 if (field.IsInitOnly)
3764 buf.Append (" readonly");
3765 if (field.IsLiteral)
3766 buf.Append (" const");
3768 buf.Append (' ').Append (GetName (field.FieldType)).Append (' ');
3769 buf.Append (field.Name);
3770 AppendFieldValue (buf, field);
3773 return buf.ToString ();
3776 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
3779 return buf.Append ("public");
3780 if (field.IsFamily || field.IsFamilyOrAssembly)
3781 return buf.Append ("protected");
3785 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
3787 // enums have a value__ field, which we ignore
3788 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
3789 field.DeclaringType.IsGenericType ())
3791 if (field.HasConstant && field.IsLiteral) {
3794 val = field.Constant;
3799 buf.Append (" = ").Append ("null");
3800 else if (val is Enum)
3801 buf.Append (" = ").Append (val.ToString ());
3802 else if (val is IFormattable) {
3803 string value = ((IFormattable)val).ToString();
3805 value = "\"" + value + "\"";
3806 buf.Append (" = ").Append (value);
3812 protected override string GetEventDeclaration (EventDefinition e)
3814 StringBuilder buf = new StringBuilder ();
3815 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
3819 AppendModifiers (buf, e.AddMethod);
3821 buf.Append (" event ");
3822 buf.Append (GetName (e.EventType)).Append (' ');
3823 buf.Append (e.Name).Append (';');
3825 return buf.ToString ();
3829 class CSharpMemberFormatter : CSharpFullMemberFormatter {
3830 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3836 class DocTypeFullMemberFormatter : MemberFormatter {
3837 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
3839 protected override char NestedTypeSeparator {
3844 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
3845 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3851 class SlashDocMemberFormatter : MemberFormatter {
3853 protected override char[] GenericTypeContainer {
3854 get {return new char[]{'{', '}'};}
3857 private bool AddTypeCount = true;
3859 private TypeReference genDeclType;
3860 private MethodReference genDeclMethod;
3862 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3864 if (type is GenericParameter) {
3866 if (genDeclType != null) {
3867 GenericParameterCollection genArgs = genDeclType.GenericParameters;
3868 for (int i = 0; i < genArgs.Count; ++i) {
3869 if (genArgs [i].Name == type.Name) {
3870 buf.Append ('`').Append (i);
3875 if (genDeclMethod != null) {
3876 GenericParameterCollection genArgs = null;
3877 if (genDeclMethod.IsGenericMethod ()) {
3878 genArgs = genDeclMethod.GenericParameters;
3879 for (int i = 0; i < genArgs.Count; ++i) {
3880 if (genArgs [i].Name == type.Name) {
3881 buf.Append ("``").Append (i);
3887 if (genDeclType == null && genDeclMethod == null) {
3888 // Probably from within an explicitly implemented interface member,
3889 // where CSC uses parameter names instead of indices (why?), e.g.
3890 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
3891 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
3892 buf.Append (type.Name);
3894 if (buf.Length == l) {
3895 throw new Exception (string.Format (
3896 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
3897 type.Name, genDeclType, genDeclMethod));
3901 base.AppendTypeName (buf, type);
3903 int numArgs = type.GenericParameters.Count;
3904 if (type.DeclaringType != null)
3905 numArgs -= type.GenericParameters.Count;
3907 buf.Append ('`').Append (numArgs);
3914 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3917 base.AppendGenericType (buf, type);
3919 AppendType (buf, type);
3923 private StringBuilder AppendType (StringBuilder buf, TypeReference type)
3925 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
3926 bool insertNested = false;
3927 int prevParamCount = 0;
3928 foreach (var decl in decls) {
3930 buf.Append (NestedTypeSeparator);
3931 insertNested = true;
3932 base.AppendTypeName (buf, decl);
3933 int argCount = DocUtils.GetGenericArgumentCount (decl);
3934 int numArgs = argCount - prevParamCount;
3935 prevParamCount = argCount;
3937 buf.Append ('`').Append (numArgs);
3942 public override string GetDeclaration (IMemberReference member)
3944 TypeReference r = member as TypeReference;
3946 return "T:" + GetTypeName (r);
3948 return base.GetDeclaration (member);
3951 protected override string GetConstructorName (MethodReference constructor)
3953 return GetMethodDefinitionName (constructor, "#ctor");
3956 protected override string GetMethodName (MethodReference method)
3959 MethodDefinition methodDef = method as MethodDefinition;
3960 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
3963 TypeReference iface;
3964 MethodReference ifaceMethod;
3965 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
3966 AddTypeCount = false;
3967 name = GetTypeName (iface) + "." + ifaceMethod.Name;
3968 AddTypeCount = true;
3970 return GetMethodDefinitionName (method, name);
3973 private string GetMethodDefinitionName (MethodReference method, string name)
3975 StringBuilder buf = new StringBuilder ();
3976 buf.Append (GetTypeName (method.DeclaringType));
3978 buf.Append (name.Replace (".", "#"));
3979 if (method.IsGenericMethod ()) {
3980 GenericParameterCollection genArgs = method.GenericParameters;
3981 if (genArgs.Count > 0)
3982 buf.Append ("``").Append (genArgs.Count);
3984 ParameterDefinitionCollection parameters = method.Parameters;
3986 genDeclType = method.DeclaringType;
3987 genDeclMethod = method;
3988 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
3992 genDeclMethod = null;
3994 return buf.ToString ();
3997 private StringBuilder AppendParameters (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinitionCollection parameters)
3999 if (parameters.Count == 0)
4004 AppendParameter (buf, genArgs, parameters [0]);
4005 for (int i = 1; i < parameters.Count; ++i) {
4007 AppendParameter (buf, genArgs, parameters [i]);
4010 return buf.Append (')');
4013 private StringBuilder AppendParameter (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinition parameter)
4015 AddTypeCount = false;
4016 buf.Append (GetTypeName (parameter.ParameterType));
4017 AddTypeCount = true;
4021 protected override string GetPropertyName (PropertyReference property)
4025 PropertyDefinition propertyDef = property as PropertyDefinition;
4026 MethodDefinition method = null;
4027 if (propertyDef != null)
4028 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
4029 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
4030 name = property.Name;
4032 TypeReference iface;
4033 MethodReference ifaceMethod;
4034 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4035 AddTypeCount = false;
4036 name = string.Join ("#", new string[]{
4037 GetTypeName (iface).Replace (".", "#"),
4038 DocUtils.GetMember (property.Name)
4040 AddTypeCount = true;
4043 StringBuilder buf = new StringBuilder ();
4044 buf.Append (GetName (property.DeclaringType));
4047 ParameterDefinitionCollection parameters = property.Parameters;
4048 if (parameters.Count > 0) {
4049 genDeclType = property.DeclaringType;
4051 GenericParameterCollection genArgs = property.DeclaringType.GenericParameters;
4052 AppendParameter (buf, genArgs, parameters [0]);
4053 for (int i = 1; i < parameters.Count; ++i) {
4055 AppendParameter (buf, genArgs, parameters [i]);
4060 return buf.ToString ();
4063 protected override string GetFieldName (FieldReference field)
4065 return string.Format ("{0}.{1}",
4066 GetName (field.DeclaringType), field.Name);
4069 protected override string GetEventName (EventReference e)
4071 return string.Format ("{0}.{1}",
4072 GetName (e.DeclaringType), e.Name);
4075 protected override string GetTypeDeclaration (TypeDefinition type)
4077 string name = GetName (type);
4083 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4085 string name = GetName (constructor);
4091 protected override string GetMethodDeclaration (MethodDefinition method)
4093 string name = GetName (method);
4096 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4097 genDeclType = method.DeclaringType;
4098 genDeclMethod = method;
4099 name += "~" + GetName (method.ReturnType.ReturnType);
4101 genDeclMethod = null;
4106 protected override string GetPropertyDeclaration (PropertyDefinition property)
4108 string name = GetName (property);
4114 protected override string GetFieldDeclaration (FieldDefinition field)
4116 string name = GetName (field);
4122 protected override string GetEventDeclaration (EventDefinition e)
4124 string name = GetName (e);
4131 class FileNameMemberFormatter : SlashDocMemberFormatter {
4132 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4137 protected override char NestedTypeSeparator {