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 var xml = File.ReadAllText (import);
134 // Ensure Unix line endings
135 xml = xml.Replace ("\r", "");
136 slashdocs = new XmlDocument();
137 slashdocs.LoadXml (xml);
139 else if (r.LocalName == "Libraries") {
140 ecmadocs = new XmlTextReader (import);
143 Error ("Unsupported XML format within {0}.", import);
146 } catch (Exception e) {
147 Environment.ExitCode = 1;
148 Error ("Could not load XML file: {0}.", e.Message);
152 // PERFORM THE UPDATES
155 DoUpdateTypes (srcPath, types, srcPath);
157 else if (opts.@namespace != null)
158 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
159 Path.Combine (dest_dir, opts.@namespace));
162 DoUpdateAssemblies (srcPath, srcPath);
164 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
167 static ExceptionLocations ParseExceptionLocations (string s)
169 ExceptionLocations loc = ExceptionLocations.Member;
172 foreach (var type in s.Split (',')) {
174 case "added": loc |= ExceptionLocations.AddedMembers; break;
175 case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
176 case "asm": loc |= ExceptionLocations.Assembly; break;
177 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
178 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
184 private void Warning (string format, params object[] args)
186 Message (TraceLevel.Warning, "mdoc: " + format, args);
189 private AssemblyDefinition LoadAssembly (string name)
191 AssemblyDefinition assembly = null;
193 assembly = AssemblyFactory.GetAssembly (name);
194 } catch (System.IO.FileNotFoundException) { }
196 if (assembly == null)
197 throw new InvalidOperationException("Assembly " + name + " not found.");
199 assembly.Resolver = assemblyResolver;
203 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
204 OrderTypeAttributes (element);
205 XmlTextWriter writer = new XmlTextWriter(output);
206 writer.Formatting = Formatting.Indented;
207 writer.Indentation = 2;
208 writer.IndentChar = ' ';
209 element.WriteTo(writer);
213 private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
215 Action<string> creator = file => {
216 using (var writer = OpenWrite (file, mode))
220 MdocFile.UpdateFile (filename, creator);
223 private static void OrderTypeAttributes (XmlElement e)
225 foreach (XmlElement type in e.SelectNodes ("//Type")) {
226 OrderTypeAttributes (type.Attributes);
230 static readonly string[] TypeAttributeOrder = {
231 "Name", "FullName", "FullNameSP", "Maintainer"
234 private static void OrderTypeAttributes (XmlAttributeCollection c)
236 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
237 for (int i = 0; i < c.Count; ++i) {
238 XmlAttribute a = c [i];
239 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
240 if (a.Name == TypeAttributeOrder [j]) {
246 for (int i = attrs.Length-1; i >= 0; --i) {
247 XmlAttribute n = attrs [i];
250 XmlAttribute r = null;
251 for (int j = i+1; j < attrs.Length; ++j) {
252 if (attrs [j] != null) {
260 c.InsertBefore (n, r);
264 private XmlDocument CreateIndexStub()
266 XmlDocument index = new XmlDocument();
268 XmlElement index_root = index.CreateElement("Overview");
269 index.AppendChild(index_root);
271 if (assemblies.Count == 0)
272 throw new Exception ("No assembly");
274 XmlElement index_assemblies = index.CreateElement("Assemblies");
275 index_root.AppendChild(index_assemblies);
277 XmlElement index_remarks = index.CreateElement("Remarks");
278 index_remarks.InnerText = "To be added.";
279 index_root.AppendChild(index_remarks);
281 XmlElement index_copyright = index.CreateElement("Copyright");
282 index_copyright.InnerText = "To be added.";
283 index_root.AppendChild(index_copyright);
285 XmlElement index_types = index.CreateElement("Types");
286 index_root.AppendChild(index_types);
291 private static void WriteNamespaceStub(string ns, string outdir) {
292 XmlDocument index = new XmlDocument();
294 XmlElement index_root = index.CreateElement("Namespace");
295 index.AppendChild(index_root);
297 index_root.SetAttribute("Name", ns);
299 XmlElement index_docs = index.CreateElement("Docs");
300 index_root.AppendChild(index_docs);
302 XmlElement index_summary = index.CreateElement("summary");
303 index_summary.InnerText = "To be added.";
304 index_docs.AppendChild(index_summary);
306 XmlElement index_remarks = index.CreateElement("remarks");
307 index_remarks.InnerText = "To be added.";
308 index_docs.AppendChild(index_remarks);
310 WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
311 writer => WriteXml (index.DocumentElement, writer));
314 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
316 var found = new HashSet<string> ();
317 foreach (AssemblyDefinition assembly in assemblies) {
318 foreach (DocsTypeInfo docsTypeInfo in GetTypes (assembly, typenames)) {
319 string relpath = DoUpdateType (docsTypeInfo.Type, basepath, dest, docsTypeInfo.EcmaDocs);
321 found.Add (docsTypeInfo.Type.FullName);
324 var notFound = from n in typenames where !found.Contains (n) select n;
326 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
329 public string DoUpdateType (TypeDefinition type, string basepath, string dest, XmlReader ecmaDocsType)
331 if (type.Namespace == null)
332 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
334 if (!IsPublic (type))
337 // Must get the A+B form of the type name.
338 string typename = GetTypeFileName(type);
340 string reltypefile = DocUtils.PathCombine (DocUtils.GetNamespace (type), typename + ".xml");
341 string typefile = Path.Combine (basepath, reltypefile);
342 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
344 string output = null;
347 } else if (dest == "-") {
350 output = Path.Combine (dest, reltypefile);
355 XmlDocument basefile = new XmlDocument();
357 basefile.Load(typefile);
358 } catch (Exception e) {
359 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
362 DoUpdateType2("Updating", basefile, type, output, false, ecmaDocsType);
365 XmlElement td = StubType(type, output, ecmaDocsType);
369 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
372 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
378 public void DoUpdateNS (string ns, string nspath, string outpath)
380 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
381 AssemblyDefinition assembly = assemblies [0];
383 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
384 XmlDocument basefile = new XmlDocument();
385 string typefile = Path.Combine(nspath, file.Name);
387 basefile.Load(typefile);
388 } catch (Exception e) {
389 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
393 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
394 TypeDefinition type = assembly.GetType(typename);
396 Warning ("Type no longer in assembly: " + typename);
400 seenTypes[type] = seenTypes;
401 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false, null);
404 // Stub types not in the directory
405 foreach (DocsTypeInfo docsTypeInfo in GetTypes (assembly, null)) {
406 TypeDefinition type = docsTypeInfo.Type;
407 if (type.Namespace != ns || seenTypes.ContainsKey(type))
410 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"), docsTypeInfo.EcmaDocs);
411 if (td == null) continue;
415 private static string GetTypeFileName (TypeReference type)
417 return filenameFormatter.GetName (type);
420 public static string GetTypeFileName (string typename)
422 StringBuilder filename = new StringBuilder (typename.Length);
426 for (int i = 0; i < typename.Length; ++i) {
427 char c = typename [i];
436 filename.Append ('`').Append ((numArgs+1).ToString());
451 return filename.ToString ();
454 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
456 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
457 index_assembly.SetAttribute ("Name", assembly.Name.Name);
458 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
460 AssemblyNameDefinition name = assembly.Name;
461 if (name.HasPublicKey) {
462 XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
463 var key = new StringBuilder (name.PublicKey.Length*3 + 2);
465 foreach (byte b in name.PublicKey)
466 key.AppendFormat ("{0,2:x2} ", b);
468 pubkey.InnerText = key.ToString ();
469 index_assembly.AppendChild (pubkey);
472 if (!string.IsNullOrEmpty (name.Culture)) {
473 XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
474 culture.InnerText = name.Culture;
475 index_assembly.AppendChild (culture);
478 MakeAttributes (index_assembly, assembly.CustomAttributes, 0);
479 parent.AppendChild(index_assembly);
482 private void DoUpdateAssemblies (string source, string dest)
484 string indexfile = dest + "/index.xml";
486 if (System.IO.File.Exists(indexfile)) {
487 index = new XmlDocument();
488 index.Load(indexfile);
491 ClearElement(index.DocumentElement, "Assembly");
492 ClearElement(index.DocumentElement, "Attributes");
494 index = CreateIndexStub();
497 string defaultTitle = "Untitled";
498 if (assemblies.Count == 1)
499 defaultTitle = assemblies[0].Name.Name;
500 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
502 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
503 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
504 index_assemblies.RemoveAll ();
507 HashSet<string> goodfiles = new HashSet<string> ();
509 foreach (AssemblyDefinition assm in assemblies) {
510 AddIndexAssembly (assm, index_assemblies);
511 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
514 SortIndexEntries (index_types);
516 CleanupFiles (dest, goodfiles);
517 CleanupIndexTypes (index_types, goodfiles);
518 CleanupExtensions (index_types);
520 WriteFile (indexfile, FileMode.Create,
521 writer => WriteXml(index.DocumentElement, writer));
524 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
526 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
528 foreach (DocsTypeInfo docTypeInfo in GetTypes (assembly, null)) {
529 TypeDefinition type = docTypeInfo.Type;
530 string typename = GetTypeFileName(type);
531 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0)
534 string reltypepath = DoUpdateType (type, source, dest, docTypeInfo.EcmaDocs);
535 if (reltypepath == null)
538 // Add namespace and type nodes into the index file as needed
539 string ns = DocUtils.GetNamespace (type);
540 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode("Namespace[@Name='" + ns + "']");
541 if (nsnode == null) {
542 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
543 nsnode.SetAttribute ("Name", ns);
544 index_types.AppendChild(nsnode);
546 string doc_typename = GetDocTypeName (type);
547 XmlElement typenode = (XmlElement)nsnode.SelectSingleNode("Type[@Name='" + typename + "']");
548 if (typenode == null) {
549 typenode = index_types.OwnerDocument.CreateElement("Type");
550 typenode.SetAttribute("Name", typename);
551 nsnode.AppendChild(typenode);
553 if (typename != doc_typename)
554 typenode.SetAttribute("DisplayName", doc_typename);
556 typenode.RemoveAttribute("DisplayName");
557 typenode.SetAttribute ("Kind", GetTypeKind (type));
559 // Ensure the namespace index file exists
560 string onsdoc = DocUtils.PathCombine (dest, type.Namespace + ".xml");
561 string nsdoc = DocUtils.PathCombine (dest, "ns-" + type.Namespace + ".xml");
562 if (File.Exists (onsdoc)) {
563 File.Move (onsdoc, nsdoc);
566 if (!File.Exists (nsdoc)) {
567 Console.WriteLine("New Namespace File: " + type.Namespace);
568 WriteNamespaceStub(type.Namespace, dest);
571 goodfiles.Add (reltypepath);
576 public TypeDefinition Type;
577 public XmlReader EcmaDocs;
579 public DocsTypeInfo (TypeDefinition type, XmlReader docs)
582 this.EcmaDocs = docs;
586 IEnumerable<Mono.Documentation.MDocUpdater.DocsTypeInfo> GetTypes (AssemblyDefinition assembly, List<string> forTypes)
588 HashSet<string> seen = null;
589 if (forTypes != null)
591 if (ecmadocs != null) {
592 seen = new HashSet<string> ();
594 while (ecmadocs.Read ()) {
595 switch (ecmadocs.Name) {
598 typeDepth = ecmadocs.Depth;
599 if (ecmadocs.NodeType != XmlNodeType.Element)
601 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
603 string typename = ecmadocs.GetAttribute ("FullName");
604 string typename2 = GetTypeFileName (typename);
605 if (forTypes != null &&
606 forTypes.BinarySearch (typename) < 0 &&
607 typename != typename2 &&
608 forTypes.BinarySearch (typename2) < 0)
611 if ((t = assembly.GetType (typename)) == null &&
612 (t = assembly.GetType (typename2)) == null)
615 if (typename != typename2)
616 seen.Add (typename2);
617 Console.WriteLine (" Import: {0}", t.FullName);
618 yield return new DocsTypeInfo (t, ecmadocs);
626 foreach (TypeDefinition type in assembly.GetTypes()) {
627 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
629 if (seen != null && seen.Contains (type.FullName))
631 yield return new DocsTypeInfo (type, null);
632 foreach (TypeDefinition nested in type.NestedTypes)
633 yield return new DocsTypeInfo (nested, null);
637 private static void SortIndexEntries (XmlElement indexTypes)
639 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
640 XmlNodeComparer c = new AttributeNameComparer ();
641 SortXmlNodes (indexTypes, namespaces, c);
643 for (int i = 0; i < namespaces.Count; ++i)
644 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
647 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
649 MyXmlNodeList l = new MyXmlNodeList (children.Count);
650 for (int i = 0; i < children.Count; ++i)
651 l.Add (children [i]);
653 for (int i = l.Count - 1; i > 0; --i) {
654 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
658 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
660 public abstract int Compare (XmlNode x, XmlNode y);
662 public int Compare (object x, object y)
664 return Compare ((XmlNode) x, (XmlNode) y);
668 class AttributeNameComparer : XmlNodeComparer {
671 public AttributeNameComparer ()
676 public AttributeNameComparer (string attribute)
678 this.attribute = attribute;
681 public override int Compare (XmlNode x, XmlNode y)
683 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
687 class VersionComparer : XmlNodeComparer {
688 public override int Compare (XmlNode x, XmlNode y)
690 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
691 string a = GetVersion (x.InnerText);
692 string b = GetVersion (y.InnerText);
693 return new Version (a).CompareTo (new Version (b));
696 static string GetVersion (string v)
698 int n = v.IndexOf ("x");
701 return v.Substring (0, n-1);
705 private static string GetTypeKind (TypeDefinition type)
708 return "Enumeration";
709 if (type.IsValueType)
711 if (type.IsInterface)
713 if (DocUtils.IsDelegate (type))
715 if (type.IsClass || type.FullName == "System.Enum") // FIXME
717 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
720 private static bool IsPublic (TypeDefinition type)
722 TypeDefinition decl = type;
723 while (decl != null) {
724 if (!(decl.IsPublic || decl.IsNestedPublic)) {
727 decl = (TypeDefinition) decl.DeclaringType;
732 private void CleanupFiles (string dest, HashSet<string> goodfiles)
734 // Look for files that no longer correspond to types
735 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
736 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
737 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
738 if (!goodfiles.Contains (relTypeFile)) {
739 XmlDocument doc = new XmlDocument ();
740 doc.Load (typefile.FullName);
741 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
742 if (UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
743 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
744 WriteXml(doc.DocumentElement, writer);
745 goodfiles.Add (relTypeFile);
748 string newname = typefile.FullName + ".remove";
749 try { System.IO.File.Delete(newname); } catch (Exception) { }
750 try { typefile.MoveTo(newname); } catch (Exception) { }
751 Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
757 private static TextWriter OpenWrite (string path, FileMode mode)
759 var w = new StreamWriter (
760 new FileStream (path, mode),
761 new UTF8Encoding (false)
767 private string[] GetAssemblyVersions ()
769 return (from a in assemblies select GetAssemblyVersion (a)).ToArray ();
772 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
774 // Look for type nodes that no longer correspond to types
775 MyXmlNodeList remove = new MyXmlNodeList ();
776 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
777 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
778 if (!goodfiles.Contains (fulltypename)) {
779 remove.Add (typenode);
782 foreach (XmlNode n in remove)
783 n.ParentNode.RemoveChild (n);
786 private void CleanupExtensions (XmlElement index_types)
788 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
789 if (extensionMethods.Count == 0) {
792 index_types.SelectSingleNode ("/Overview").RemoveChild (e);
796 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
797 index_types.SelectSingleNode ("/Overview").AppendChild (e);
801 extensionMethods.Sort (DefaultExtensionMethodComparer);
802 foreach (XmlNode m in extensionMethods) {
803 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
807 class ExtensionMethodComparer : XmlNodeComparer {
808 public override int Compare (XmlNode x, XmlNode y)
810 XmlNode xLink = x.SelectSingleNode ("Member/Link");
811 XmlNode yLink = y.SelectSingleNode ("Member/Link");
813 int n = xLink.Attributes ["Type"].Value.CompareTo (
814 yLink.Attributes ["Type"].Value);
817 n = xLink.Attributes ["Member"].Value.CompareTo (
818 yLink.Attributes ["Member"].Value);
819 if (n == 0 && !object.ReferenceEquals (x, y))
820 throw new InvalidOperationException ("Duplicate extension method found!");
825 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
827 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince, XmlReader ecmaDocsType)
829 Console.WriteLine(message + ": " + type.FullName);
831 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
833 // Update type metadata
834 UpdateType(basefile.DocumentElement, type, ecmaDocsType);
836 if (ecmaDocsType != null) {
837 while (ecmaDocsType.Name != "Members" && ecmaDocsType.Read ()) {
840 if (ecmaDocsType.IsEmptyElement)
844 // Update existing members. Delete member nodes that no longer should be there,
845 // and remember what members are already documented so we don't add them again.
847 MyXmlNodeList todelete = new MyXmlNodeList ();
848 foreach (DocsNodeInfo info in GetDocumentationMembers (basefile, type, ecmaDocsType)) {
849 XmlElement oldmember = info.Node;
850 IMemberReference oldmember2 = info.Member;
851 string sig = oldmember2 != null ? MakeMemberSignature(oldmember2) : null;
853 // Interface implementations and overrides are deleted from the docs
854 // unless the overrides option is given.
855 if (oldmember2 != null && sig == null)
858 // Deleted (or signature changed)
859 if (oldmember2 == null) {
860 if (UpdateAssemblyVersions (oldmember, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
862 DeleteMember ("Member Removed", output, oldmember, todelete);
867 if (seenmembers.ContainsKey (sig)) {
868 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
869 // ignore, already seen
871 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
872 DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
874 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
878 // Update signature information
881 seenmembers.Add (sig, oldmember);
883 foreach (XmlElement oldmember in todelete)
884 oldmember.ParentNode.RemoveChild (oldmember);
887 if (!DocUtils.IsDelegate (type)) {
888 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
889 foreach (IMemberReference m in type.GetMembers()) {
890 if (m is TypeDefinition) continue;
892 string sig = MakeMemberSignature(m);
893 if (sig == null) continue;
894 if (seenmembers.ContainsKey(sig)) continue;
896 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
897 if (mm == null) continue;
898 members.AppendChild( mm );
900 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
905 // Import code snippets from files
906 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
907 if (!(code is XmlElement)) continue;
908 string file = ((XmlElement)code).GetAttribute("src");
909 string lang = ((XmlElement)code).GetAttribute("lang");
911 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
913 code.InnerText = src;
917 if (insertSince && since != null) {
918 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
919 docs.AppendChild (CreateSinceNode (basefile));
923 XmlElement d = basefile.DocumentElement ["Docs"];
924 XmlElement m = basefile.DocumentElement ["Members"];
925 if (d != null && m != null)
926 basefile.DocumentElement.InsertBefore (
927 basefile.DocumentElement.RemoveChild (d), m);
932 WriteXml(basefile.DocumentElement, Console.Out);
934 FileInfo file = new FileInfo (output);
935 if (!file.Directory.Exists) {
936 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
937 file.Directory.Create ();
939 WriteFile (output, FileMode.Create,
940 writer => WriteXml(basefile.DocumentElement, writer));
944 private string GetCodeSource (string lang, string file)
947 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
948 // Grab the specified region
949 string region = "#region " + file.Substring (anchorStart + 4);
950 file = file.Substring (0, anchorStart + 3);
952 using (StreamReader reader = new StreamReader (file)) {
954 StringBuilder src = new StringBuilder ();
956 while ((line = reader.ReadLine ()) != null) {
957 if (line.Trim() == region) {
958 indent = line.IndexOf (region);
961 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
966 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
969 return src.ToString ();
971 } catch (Exception e) {
972 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
973 file, region, show_exceptions ? e.ToString () : e.Message);
978 using (StreamReader reader = new StreamReader (file))
979 return reader.ReadToEnd ();
980 } catch (Exception e) {
981 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
986 private IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type, XmlReader ecmaDocsMembers)
988 if (ecmaDocsMembers != null) {
989 int membersDepth = ecmaDocsMembers.Depth;
991 while (go && ecmaDocsMembers.Read ()) {
992 switch (ecmaDocsMembers.Name) {
994 if (membersDepth != ecmaDocsMembers.Depth - 1 || ecmaDocsMembers.NodeType != XmlNodeType.Element)
996 DocumentationMember dm = new DocumentationMember (ecmaDocsMembers);
997 string xp = GetXPathForMember (dm);
998 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
1000 if (oldmember == null) {
1001 m = GetMember (type, dm);
1003 Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
1004 type.FullName, dm.MemberSignatures ["C#"]);
1005 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
1008 // oldmember lookup may have failed due to type parameter renames.
1010 oldmember = (XmlElement) basefile.SelectSingleNode (GetXPathForMember (m));
1011 if (oldmember == null) {
1012 XmlElement members = WriteElement(basefile.DocumentElement, "Members");
1013 oldmember = basefile.CreateElement ("Member");
1014 oldmember.SetAttribute ("MemberName", dm.MemberName);
1015 members.AppendChild (oldmember);
1016 foreach (string key in Sort (dm.MemberSignatures.Keys)) {
1017 XmlElement ms = basefile.CreateElement ("MemberSignature");
1018 ms.SetAttribute ("Language", key);
1019 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
1020 oldmember.AppendChild (ms);
1022 oldmember.SetAttribute ("__monodocer-seen__", "true");
1023 Console.WriteLine ("Member Added: {0}", MakeMemberSignature (m));
1028 m = GetMember (type, new DocumentationMember (oldmember));
1030 Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
1031 type.FullName, dm.MemberSignatures ["C#"]);
1034 oldmember.SetAttribute ("__monodocer-seen__", "true");
1036 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
1037 if (ecmaDocsMembers.Name != "Docs")
1038 throw new InvalidOperationException ("Found " + ecmaDocsMembers.Name + "; expected <Docs/>!");
1039 node.EcmaDocs = ecmaDocsMembers;
1044 if (membersDepth == ecmaDocsMembers.Depth && ecmaDocsMembers.NodeType == XmlNodeType.EndElement) {
1051 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
1052 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
1053 oldmember.RemoveAttribute ("__monodocer-seen__");
1056 IMemberReference m = GetMember (type, new DocumentationMember (oldmember));
1058 yield return new DocsNodeInfo (oldmember);
1061 yield return new DocsNodeInfo (oldmember, m);
1066 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
1068 string format = output != null
1069 ? "{0}: File='{1}'; Signature='{4}'"
1070 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1074 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1075 member.Attributes ["MemberName"].Value,
1076 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
1077 if (!delete && MemberDocsHaveUserContent (member)) {
1078 Warning ("Member deletions must be enabled with the --delete option.");
1080 todelete.Add (member);
1085 class MemberComparer : XmlNodeComparer {
1086 public override int Compare (XmlNode x, XmlNode y)
1089 string xMemberName = x.Attributes ["MemberName"].Value;
1090 string yMemberName = y.Attributes ["MemberName"].Value;
1092 // generic methods *end* with '>'
1093 // it's possible for explicitly implemented generic interfaces to
1094 // contain <...> without being a generic method
1095 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1096 (r = xMemberName.CompareTo (yMemberName)) != 0)
1100 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1101 xMemberName = xMemberName.Substring (0, lt);
1102 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1103 yMemberName = yMemberName.Substring (0, lt);
1104 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1107 // if @MemberName matches, then it's either two different types of
1108 // members sharing the same name, e.g. field & property, or it's an
1109 // overloaded method.
1110 // for different type, sort based on MemberType value.
1111 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1112 y.SelectSingleNode ("MemberType").InnerText);
1116 // same type -- must be an overloaded method. Sort based on type
1117 // parameter count, then parameter count, then by the parameter
1119 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1120 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1121 if (xTypeParams.Count != yTypeParams.Count)
1122 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1123 for (int i = 0; i < xTypeParams.Count; ++i) {
1124 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1125 yTypeParams [i].Attributes ["Name"].Value);
1130 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1131 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1132 if (xParams.Count != yParams.Count)
1133 return xParams.Count <= yParams.Count ? -1 : 1;
1134 for (int i = 0; i < xParams.Count; ++i) {
1135 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1136 yParams [i].Attributes ["Type"].Value);
1140 // all parameters match, but return value might not match if it was
1141 // changed between one version and another.
1142 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1143 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1144 if (xReturn != null && yReturn != null) {
1145 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1154 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1156 private static void SortTypeMembers (XmlNode members)
1158 if (members == null)
1160 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1163 private static bool MemberDocsHaveUserContent (XmlNode e)
1165 e = (XmlElement)e.SelectSingleNode("Docs");
1166 if (e == null) return false;
1167 foreach (XmlElement d in e.SelectNodes("*"))
1168 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1173 // UPDATE HELPER FUNCTIONS
1175 private static IMemberReference GetMember (TypeDefinition type, DocumentationMember member)
1177 string membertype = member.MemberType;
1179 string returntype = member.ReturnType;
1181 string docName = member.MemberName;
1182 string[] docTypeParams = GetTypeParameters (docName);
1184 // Loop through all members in this type with the same name
1185 foreach (IMemberReference mi in GetReflectionMembers (type, docName)) {
1186 if (mi is TypeDefinition) continue;
1187 if (GetMemberType(mi) != membertype) continue;
1189 string sig = MakeMemberSignature(mi);
1190 if (sig == null) continue; // not publicly visible
1192 ParameterDefinitionCollection pis = null;
1193 string[] typeParams = null;
1194 if (mi is MethodDefinition) {
1195 MethodDefinition mb = (MethodDefinition) mi;
1196 pis = mb.Parameters;
1197 if (docTypeParams != null && mb.IsGenericMethod ()) {
1198 GenericParameterCollection args = mb.GenericParameters;
1199 if (args.Count == docTypeParams.Length) {
1200 typeParams = args.Cast<GenericParameter> ().Select (p => p.Name).ToArray ();
1204 else if (mi is PropertyDefinition)
1205 pis = ((PropertyDefinition)mi).Parameters;
1207 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
1208 int pcount = pis == null ? 0 : pis.Count;
1209 if (mcount != pcount)
1212 MethodDefinition mDef = mi as MethodDefinition;
1213 if (mDef != null && !mDef.IsConstructor) {
1214 // Casting operators can overload based on return type.
1215 if (returntype != GetReplacedString (
1216 GetDocTypeFullName (((MethodDefinition)mi).ReturnType.ReturnType),
1217 typeParams, docTypeParams)) {
1225 for (int i = 0; i < pis.Count; i++) {
1226 string paramType = GetReplacedString (
1227 GetDocParameterType (pis [i].ParameterType),
1228 typeParams, docTypeParams);
1229 if (paramType != (string) member.Parameters [i]) {
1234 if (!good) continue;
1242 private static IEnumerable<IMemberReference> GetReflectionMembers (TypeDefinition type, string docName)
1244 // need to worry about 4 forms of //@MemberName values:
1245 // 1. "Normal" (non-generic) member names: GetEnumerator
1247 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
1248 // - try as-is, and try type.member (due to "kludge" for property
1250 // 3. "Normal" Generic member names: Sort<T> (CSC)
1251 // - need to remove generic parameters --> "Sort"
1252 // 4. Explicitly-implemented interface members for generic interfaces:
1253 // -- System.Collections.Generic.IEnumerable<T>.Current
1254 // - Try as-is, and try type.member, *keeping* the generic parameters.
1255 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
1256 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
1257 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
1258 // this as (1) or (2).
1259 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
1261 foreach (IMemberReference mi in type.GetMembers (docName))
1263 if (CountChars (docName, '.') > 0)
1264 // might be a property; try only type.member instead of
1265 // namespace.type.member.
1266 foreach (IMemberReference mi in
1267 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
1274 int startLt, startType, startMethod;
1275 startLt = startType = startMethod = -1;
1276 for (int i = 0; i < docName.Length; ++i) {
1277 switch (docName [i]) {
1286 if (numLt == 0 && (i + 1) < docName.Length)
1287 // there's another character in docName, so this <...> sequence is
1288 // probably part of a generic type -- case 4.
1292 startType = startMethod;
1298 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
1300 foreach (IMemberReference mi in type.GetMembers (refName))
1304 foreach (IMemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
1307 // If we _still_ haven't found it, we've hit another generic naming issue:
1308 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
1309 // explicitly-implemented METHOD names (not properties), e.g.
1310 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
1311 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
1312 // which the XML docs will contain.
1314 // Alas, we can't derive the Mono name from docName, so we need to iterate
1315 // over all member names, convert them into CSC format, and compare... :-(
1318 foreach (IMemberReference mi in type.GetMembers ()) {
1319 if (GetMemberName (mi) == docName)
1324 static string[] GetTypeParameters (string docName)
1326 if (docName [docName.Length-1] != '>')
1328 StringList types = new StringList ();
1329 int endToken = docName.Length-2;
1330 int i = docName.Length-2;
1332 if (docName [i] == ',' || docName [i] == '<') {
1333 types.Add (docName.Substring (i + 1, endToken - i));
1336 if (docName [i] == '<')
1341 return types.ToArray ();
1344 static string GetReplacedString (string typeName, string[] from, string[] to)
1348 for (int i = 0; i < from.Length; ++i)
1349 typeName = typeName.Replace (from [i], to [i]);
1353 // CREATE A STUB DOCUMENTATION FILE
1355 public XmlElement StubType (TypeDefinition type, string output, XmlReader ecmaDocsType)
1357 string typesig = MakeTypeSignature(type);
1358 if (typesig == null) return null; // not publicly visible
1360 XmlDocument doc = new XmlDocument();
1361 XmlElement root = doc.CreateElement("Type");
1362 doc.AppendChild (root);
1364 DoUpdateType2 ("New Type", doc, type, output, true, ecmaDocsType);
1369 private XmlElement CreateSinceNode (XmlDocument doc)
1371 XmlElement s = doc.CreateElement ("since");
1372 s.SetAttribute ("version", since);
1376 // STUBBING/UPDATING FUNCTIONS
1378 public void UpdateType (XmlElement root, TypeDefinition type, XmlReader ecmaDocsType)
1380 root.SetAttribute("Name", GetDocTypeName (type));
1381 root.SetAttribute("FullName", GetDocTypeFullName (type));
1383 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Language", "C#");
1384 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Value", MakeTypeSignature(type));
1386 XmlElement ass = WriteElement(root, "AssemblyInfo");
1387 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1388 if (!no_assembly_versions) {
1389 UpdateAssemblyVersions (root, type, true);
1392 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1393 foreach (var version in versions)
1394 ass.RemoveChild (version);
1396 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1397 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1399 ClearElement(ass, "AssemblyCulture");
1401 // Why-oh-why do we put assembly attributes in each type file?
1402 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1403 // since they're outdated in current docs, and a waste of space.
1404 //MakeAttributes(ass, type.Assembly, true);
1405 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1406 if (assattrs != null)
1407 ass.RemoveChild(assattrs);
1409 NormalizeWhitespace(ass);
1411 if (type.IsGenericType ()) {
1412 MakeTypeParameters (root, type.GenericParameters);
1414 ClearElement(root, "TypeParameters");
1417 if (type.BaseType != null) {
1418 XmlElement basenode = WriteElement(root, "Base");
1420 string basetypename = GetDocTypeFullName (type.BaseType);
1421 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1422 WriteElementText(root, "Base/BaseTypeName", basetypename);
1424 // Document how this type instantiates the generic parameters of its base type
1425 TypeReference origBase = type.BaseType.GetOriginalType ();
1426 if (origBase.IsGenericType ()) {
1427 ClearElement(basenode, "BaseTypeArguments");
1428 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1429 GenericArgumentCollection baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1430 GenericParameterCollection baseGenParams = origBase.GenericParameters;
1431 if (baseGenArgs.Count != baseGenParams.Count)
1432 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1433 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1434 GenericParameter param = baseGenParams [i];
1435 TypeReference value = baseGenArgs [i];
1437 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1438 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1439 bta.AppendChild(arg);
1440 arg.SetAttribute ("TypeParamName", param.Name);
1441 arg.InnerText = GetDocTypeFullName (value);
1445 ClearElement(root, "Base");
1448 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1449 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1450 List<string> interface_names = userInterfaces
1451 .Select (iface => GetDocTypeFullName (iface))
1455 XmlElement interfaces = WriteElement(root, "Interfaces");
1456 interfaces.RemoveAll();
1457 foreach (string iname in interface_names) {
1458 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1459 interfaces.AppendChild(iface);
1460 WriteElementText(iface, "InterfaceName", iname);
1463 ClearElement(root, "Interfaces");
1466 MakeAttributes (root, type.CustomAttributes, 0);
1468 if (DocUtils.IsDelegate (type)) {
1469 MakeTypeParameters (root, type.GenericParameters);
1470 MakeParameters(root, type.GetMethod("Invoke").Parameters);
1471 MakeReturnValue(root, type.GetMethod("Invoke"));
1474 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1475 if (ecmaDocsType != null) {
1476 if (ecmaDocsType.Name != "Docs") {
1477 int depth = ecmaDocsType.Depth;
1478 while (ecmaDocsType.Read ()) {
1479 if (ecmaDocsType.Name == "Docs" && ecmaDocsType.Depth == depth + 1)
1483 if (!ecmaDocsType.IsStartElement ("Docs"))
1484 throw new InvalidOperationException ("Found " + ecmaDocsType.Name + "; expecting <Docs/>!");
1485 typeInfo.EcmaDocs = ecmaDocsType;
1487 MakeDocNode (typeInfo);
1489 if (!DocUtils.IsDelegate (type))
1490 WriteElement (root, "Members");
1492 NormalizeWhitespace(root);
1495 static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1497 List<T> l = new List<T> (list);
1502 private void UpdateMember (DocsNodeInfo info)
1504 XmlElement me = (XmlElement) info.Node;
1505 IMemberReference mi = info.Member;
1506 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Language", "C#");
1507 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Value", MakeMemberSignature(mi));
1509 WriteElementText(me, "MemberType", GetMemberType(mi));
1511 if (!no_assembly_versions) {
1512 UpdateAssemblyVersions (me, mi, true);
1515 ClearElement (me, "AssemblyInfo");
1518 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1520 MakeAttributes (me, p.CustomAttributes, 0);
1522 PropertyReference pr = mi as PropertyReference;
1524 PropertyDefinition pd = pr.Resolve ();
1525 if (pd.GetMethod != null)
1526 MakeAttributes (me, pd.GetMethod.CustomAttributes, AttributeFlags.KeepExistingAttributes, "get: ");
1527 if (pd.SetMethod != null)
1528 MakeAttributes (me, pd.SetMethod.CustomAttributes, AttributeFlags.KeepExistingAttributes, "set: ");
1530 EventReference er = mi as EventReference;
1532 EventDefinition ed = er.Resolve ();
1533 if (ed.AddMethod != null)
1534 MakeAttributes (me, ed.AddMethod.CustomAttributes, AttributeFlags.KeepExistingAttributes, "add: ");
1535 if (ed.RemoveMethod != null)
1536 MakeAttributes (me, ed.RemoveMethod.CustomAttributes, AttributeFlags.KeepExistingAttributes, "remove: ");
1539 MakeReturnValue(me, mi);
1540 if (mi is MethodReference) {
1541 MethodReference mb = (MethodReference) mi;
1542 if (mb.IsGenericMethod ())
1543 MakeTypeParameters (me, mb.GenericParameters);
1545 MakeParameters(me, mi);
1548 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1549 WriteElementText(me, "MemberValue", fieldValue);
1551 info.Node = WriteElement (me, "Docs");
1553 UpdateExtensionMethods (me, info);
1556 static readonly string[] ValidExtensionMembers = {
1565 static readonly string[] ValidExtensionDocMembers = {
1571 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1573 MethodDefinition me = info.Member as MethodDefinition;
1576 if (info.Parameters.Count < 1)
1578 if (!DocUtils.IsExtensionMethod (me))
1581 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1582 XmlNode member = e.CloneNode (true);
1583 em.AppendChild (member);
1584 RemoveExcept (member, ValidExtensionMembers);
1585 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1586 WriteElementText (member, "MemberType", "ExtensionMethod");
1587 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1588 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1589 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1590 member.AppendChild (link);
1591 AddTargets (em, info);
1593 extensionMethods.Add (em);
1596 private static void RemoveExcept (XmlNode node, string[] except)
1600 MyXmlNodeList remove = null;
1601 foreach (XmlNode n in node.ChildNodes) {
1602 if (Array.BinarySearch (except, n.Name) < 0) {
1604 remove = new MyXmlNodeList ();
1609 foreach (XmlNode n in remove)
1610 node.RemoveChild (n);
1613 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1615 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1616 member.PrependChild (targets);
1617 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1618 AppendElementAttributeText (targets, "Target", "Type",
1619 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1622 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1623 ConstraintCollection constraints = gp.Constraints;
1624 if (constraints.Count == 0)
1625 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1627 foreach (TypeReference c in constraints)
1628 AppendElementAttributeText(targets, "Target", "Type",
1629 slashdocFormatter.GetDeclaration (c));
1633 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1636 TypeDefinition type = field.DeclaringType.Resolve ();
1637 if (type != null && type.IsEnum) return false;
1639 if (type != null && type.IsGenericType ()) return false;
1640 if (!field.HasConstant)
1642 if (field.IsLiteral) {
1643 object val = field.Constant;
1644 if (val == null) value = "null";
1645 else if (val is Enum) value = val.ToString();
1646 else if (val is IFormattable) {
1647 value = ((IFormattable)val).ToString();
1649 value = "\"" + value + "\"";
1651 if (value != null && value != "")
1657 // XML HELPER FUNCTIONS
1659 private static XmlElement WriteElement(XmlNode parent, string element) {
1660 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1662 string[] path = element.Split('/');
1663 foreach (string p in path) {
1664 ret = (XmlElement)parent.SelectSingleNode(p);
1667 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1668 ename = ename.Substring(0, ename.IndexOf('['));
1669 ret = parent.OwnerDocument.CreateElement(ename);
1670 parent.AppendChild(ret);
1679 private static void WriteElementText(XmlNode parent, string element, string value) {
1680 XmlElement node = WriteElement(parent, element);
1681 node.InnerText = value;
1684 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1686 XmlElement n = parent.OwnerDocument.CreateElement (element);
1687 parent.AppendChild (n);
1688 n.InnerText = value;
1692 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1694 XmlElement n = parent.OwnerDocument.CreateElement (element);
1695 parent.AppendChild (n);
1696 n.SetAttribute (attribute, value);
1700 private static XmlNode CopyNode (XmlNode source, XmlNode dest)
1702 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1703 dest.AppendChild (copy);
1707 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1708 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1711 node = WriteElement(parent, element);
1712 node.InnerText = value;
1714 private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
1715 XmlElement node = WriteElement(parent, element);
1716 if (node.GetAttribute(attribute) == value) return;
1717 node.SetAttribute(attribute, value);
1719 private static void ClearElement(XmlElement parent, string name) {
1720 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1722 parent.RemoveChild(node);
1725 // DOCUMENTATION HELPER FUNCTIONS
1727 private void MakeDocNode (DocsNodeInfo info)
1729 List<GenericParameter> genericParams = info.GenericParameters;
1730 ParameterDefinitionCollection parameters = info.Parameters;
1731 TypeReference returntype = info.ReturnType;
1732 bool returnisreturn = info.ReturnIsReturn;
1733 XmlElement e = info.Node;
1734 bool addremarks = info.AddRemarks;
1736 WriteElementInitialText(e, "summary", "To be added.");
1738 if (parameters != null) {
1739 string[] values = new string [parameters.Count];
1740 for (int i = 0; i < values.Length; ++i)
1741 values [i] = parameters [i].Name;
1742 UpdateParameters (e, "param", values);
1745 if (genericParams != null) {
1746 string[] values = new string [genericParams.Count];
1747 for (int i = 0; i < values.Length; ++i)
1748 values [i] = genericParams [i].Name;
1749 UpdateParameters (e, "typeparam", values);
1752 string retnodename = null;
1753 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
1754 retnodename = returnisreturn ? "returns" : "value";
1755 string retnodename_other = !returnisreturn ? "returns" : "value";
1757 // If it has a returns node instead of a value node, change its name.
1758 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1759 if (retother != null) {
1760 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1761 foreach (XmlNode node in retother)
1762 retnode.AppendChild(node.CloneNode(true));
1763 e.ReplaceChild(retnode, retother);
1765 WriteElementInitialText(e, retnodename, "To be added.");
1768 ClearElement(e, "returns");
1769 ClearElement(e, "value");
1773 WriteElementInitialText(e, "remarks", "To be added.");
1775 if (exceptions.HasValue && info.Member != null &&
1776 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
1777 UpdateExceptions (e, info.Member);
1780 if (info.EcmaDocs != null) {
1781 XmlReader r = info.EcmaDocs;
1782 int depth = r.Depth;
1783 r.ReadStartElement ("Docs");
1785 if (r.Name == "Docs") {
1786 if (r.Depth == depth && r.NodeType == XmlNodeType.EndElement)
1789 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
1791 if (!r.IsStartElement ())
1796 string name = r.GetAttribute ("name");
1799 XmlNode doc = e.SelectSingleNode (
1800 r.Name + "[@name='" + name + "']");
1801 string value = r.ReadInnerXml ();
1803 doc.InnerXml = value.Replace ("\r", "");
1810 string name = r.Name;
1811 string cref = r.GetAttribute ("cref");
1814 XmlNode doc = e.SelectSingleNode (
1815 r.Name + "[@cref='" + cref + "']");
1816 string value = r.ReadInnerXml ().Replace ("\r", "");
1818 doc.InnerXml = value;
1820 XmlElement n = e.OwnerDocument.CreateElement (name);
1821 n.SetAttribute ("cref", cref);
1828 string name = r.Name;
1829 string xpath = r.Name;
1830 StringList attributes = new StringList (r.AttributeCount);
1831 if (r.MoveToFirstAttribute ()) {
1833 attributes.Add ("@" + r.Name + "=\"" + r.Value + "\"");
1834 } while (r.MoveToNextAttribute ());
1837 if (attributes.Count > 0) {
1838 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
1840 XmlNode doc = e.SelectSingleNode (xpath);
1841 string value = r.ReadInnerXml ().Replace ("\r", "");
1843 doc.InnerXml = value;
1846 XmlElement n = e.OwnerDocument.CreateElement (name);
1848 foreach (string a in attributes) {
1849 int eq = a.IndexOf ('=');
1850 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
1859 if (info.SlashDocs != null) {
1860 XmlNode elem = info.SlashDocs;
1862 if (elem.SelectSingleNode("summary") != null)
1863 ClearElement(e, "summary");
1864 if (elem.SelectSingleNode("remarks") != null)
1865 ClearElement(e, "remarks");
1866 if (elem.SelectSingleNode("value") != null)
1867 ClearElement(e, "value");
1868 if (retnodename != null && elem.SelectSingleNode(retnodename) != null)
1869 ClearElement(e, retnodename);
1871 foreach (XmlNode child in elem.ChildNodes) {
1872 switch (child.Name) {
1875 XmlAttribute name = child.Attributes ["name"];
1878 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
1880 p2.InnerXml = child.InnerXml;
1885 case "permission": {
1886 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
1889 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
1891 a = e.OwnerDocument.CreateElement (child.Name);
1892 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
1895 a.InnerXml = child.InnerXml;
1899 XmlAttribute cref = child.Attributes ["cref"];
1902 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
1904 a = e.OwnerDocument.CreateElement ("altmember");
1905 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
1911 CopyNode (child, e);
1918 OrderDocsNodes (e, e.ChildNodes);
1919 NormalizeWhitespace(e);
1922 static readonly string[] DocsNodeOrder = {
1923 "typeparam", "param", "summary", "returns", "value", "remarks",
1926 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
1928 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1929 for (int i = 0; i < DocsNodeOrder.Length; ++i) {
1930 for (int j = 0; j < children.Count; ++j) {
1931 XmlNode c = children [j];
1932 if (c.Name == DocsNodeOrder [i]) {
1933 newChildren.Add (c);
1937 if (newChildren.Count >= 0)
1938 docs.PrependChild ((XmlNode) newChildren [0]);
1939 for (int i = 1; i < newChildren.Count; ++i) {
1940 XmlNode prev = (XmlNode) newChildren [i-1];
1941 XmlNode cur = (XmlNode) newChildren [i];
1942 docs.RemoveChild (cur);
1943 docs.InsertAfter (cur, prev);
1948 private void UpdateParameters (XmlElement e, string element, string[] values)
1950 if (values != null) {
1951 XmlNode[] paramnodes = new XmlNode[values.Length];
1953 // Some documentation had param nodes with leading spaces.
1954 foreach (XmlElement paramnode in e.SelectNodes(element)){
1955 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
1958 // If a member has only one parameter, we can track changes to
1959 // the name of the parameter easily.
1960 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
1961 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
1964 bool reinsert = false;
1966 // Pick out existing and still-valid param nodes, and
1967 // create nodes for parameters not in the file.
1968 Hashtable seenParams = new Hashtable();
1969 for (int pi = 0; pi < values.Length; pi++) {
1970 string p = values [pi];
1973 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
1974 if (paramnodes[pi] != null) continue;
1976 XmlElement pe = e.OwnerDocument.CreateElement(element);
1977 pe.SetAttribute("name", p);
1978 pe.InnerText = "To be added.";
1979 paramnodes[pi] = pe;
1983 // Remove parameters that no longer exist and check all params are in the right order.
1985 MyXmlNodeList todelete = new MyXmlNodeList ();
1986 foreach (XmlElement paramnode in e.SelectNodes(element)) {
1987 string name = paramnode.GetAttribute("name");
1988 if (!seenParams.ContainsKey(name)) {
1989 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1990 Warning ("The following param node can only be deleted if the --delete option is given: ");
1991 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
1993 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
1994 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1998 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
1999 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2000 e.ParentNode.Attributes ["MemberName"].Value,
2003 Warning ("\tValue={0}", paramnode.OuterXml);
2005 todelete.Add (paramnode);
2010 if ((int)seenParams[name] != idx)
2016 foreach (XmlNode n in todelete) {
2017 n.ParentNode.RemoveChild (n);
2020 // Re-insert the parameter nodes at the top of the doc section.
2022 for (int pi = values.Length-1; pi >= 0; pi--)
2023 e.PrependChild(paramnodes[pi]);
2025 // Clear all existing param nodes
2026 foreach (XmlNode paramnode in e.SelectNodes(element)) {
2027 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2028 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
2029 Console.WriteLine(paramnode.OuterXml);
2031 paramnode.ParentNode.RemoveChild(paramnode);
2037 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
2039 string existingName = pe.GetAttribute ("name");
2040 pe.SetAttribute ("name", newName);
2041 if (existingName == newName)
2043 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
2044 if (paramref.GetAttribute ("name").Trim () == existingName)
2045 paramref.SetAttribute ("name", newName);
2048 class CrefComparer : XmlNodeComparer {
2050 public CrefComparer ()
2054 public override int Compare (XmlNode x, XmlNode y)
2056 string xType = x.Attributes ["cref"].Value;
2057 string yType = y.Attributes ["cref"].Value;
2058 string xNamespace = GetNamespace (xType);
2059 string yNamespace = GetNamespace (yType);
2061 int c = xNamespace.CompareTo (yNamespace);
2064 return xType.CompareTo (yType);
2067 static string GetNamespace (string type)
2069 int n = type.LastIndexOf ('.');
2071 return type.Substring (0, n);
2072 return string.Empty;
2076 private void UpdateExceptions (XmlNode docs, IMemberReference member)
2078 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
2079 string cref = slashdocFormatter.GetDeclaration (source.Exception);
2080 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
2083 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
2084 e.SetAttribute ("cref", cref);
2085 e.InnerXml = "To be added; from: <see cref=\"" +
2086 string.Join ("\" />, <see cref=\"",
2087 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
2090 docs.AppendChild (e);
2092 SortXmlNodes (docs, docs.SelectNodes ("exception"),
2093 new CrefComparer ());
2096 private static void NormalizeWhitespace(XmlElement e) {
2097 // Remove all text and whitespace nodes from the element so it
2098 // is outputted with nice indentation and no blank lines.
2099 ArrayList deleteNodes = new ArrayList();
2100 foreach (XmlNode n in e)
2101 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2103 foreach (XmlNode n in deleteNodes)
2104 n.ParentNode.RemoveChild(n);
2107 private static bool UpdateAssemblyVersions (XmlElement root, IMemberReference member, bool add)
2109 TypeDefinition type = member as TypeDefinition;
2111 type = member.DeclaringType as TypeDefinition;
2112 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
2115 private static string GetAssemblyVersion (AssemblyDefinition assembly)
2117 return assembly.Name.Version.ToString();
2120 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
2122 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
2124 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2125 root.AppendChild(e);
2127 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
2128 .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
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",
2172 enum AttributeFlags {
2174 KeepExistingAttributes = 0x1,
2177 private void MakeAttributes (XmlElement root, CustomAttributeCollection attributes, AttributeFlags flags)
2179 MakeAttributes (root, attributes, flags, null);
2182 private void MakeAttributes (XmlElement root, CustomAttributeCollection attributes, AttributeFlags flags, string prefix)
2184 bool keepExisting = (flags & AttributeFlags.KeepExistingAttributes) != 0;
2185 if (attributes.Count == 0) {
2187 ClearElement(root, "Attributes");
2192 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2193 if (e != null && !keepExisting)
2196 e = root.OwnerDocument.CreateElement("Attributes");
2198 foreach (CustomAttribute attribute in attributes.Cast<CustomAttribute> ()
2199 .OrderBy (ca => ca.Constructor.DeclaringType.FullName)) {
2200 if (!attribute.Resolve ()) {
2202 Warning ("warning: could not resolve type {0}.",
2203 attribute.Constructor.DeclaringType.FullName);
2205 TypeDefinition attrType = attribute.Constructor.DeclaringType as TypeDefinition;
2206 if (attrType != null && !IsPublic (attrType))
2208 if (slashdocFormatter.GetName (attribute.Constructor.DeclaringType) == null)
2211 if (Array.IndexOf (IgnorableAttributes, attribute.Constructor.DeclaringType.FullName) >= 0)
2216 StringList fields = new StringList ();
2218 ParameterDefinitionCollection parameters = attribute.Constructor.Parameters;
2219 for (int i = 0; i < attribute.ConstructorParameters.Count; ++i) {
2220 fields.Add (MakeAttributesValueString (
2221 attribute.ConstructorParameters [i],
2222 parameters [i].ParameterType));
2225 (from de in attribute.Fields.Cast<DictionaryEntry> ()
2226 select new { Type=attribute.GetFieldType (de.Key.ToString ()), Name=de.Key, Value=de.Value })
2228 (from de in attribute.Properties.Cast<DictionaryEntry> ()
2229 select new { Type=attribute.GetPropertyType (de.Key.ToString ()), Name=de.Key, Value=de.Value }))
2230 .OrderBy (v => v.Name);
2231 foreach (var d in namedArgs)
2232 fields.Add (string.Format ("{0}={1}", d.Name,
2233 MakeAttributesValueString (d.Value, d.Type)));
2235 string a2 = String.Join(", ", fields.ToArray ());
2236 if (a2 != "") a2 = "(" + a2 + ")";
2238 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2241 string name = attribute.Constructor.DeclaringType.FullName;
2242 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
2243 WriteElementText(ae, "AttributeName", prefix + name + a2);
2246 if (b && e.ParentNode == null)
2247 root.AppendChild(e);
2249 ClearElement(root, "Attributes");
2251 NormalizeWhitespace(e);
2254 private static string MakeAttributesValueString (object v, TypeReference valueType)
2258 if (valueType.FullName == "System.Type")
2259 return "typeof(" + v.ToString () + ")";
2260 if (valueType.FullName == "System.String")
2261 return "\"" + v.ToString () + "\"";
2263 return (bool)v ? "true" : "false";
2264 TypeDefinition valueDef = valueType.Resolve ();
2265 if (valueDef == null || !valueDef.IsEnum)
2266 return v.ToString ();
2267 string typename = GetDocTypeFullName (valueType);
2268 var values = GetEnumerationValues (valueDef);
2269 long c = ToInt64 (v);
2270 if (values.ContainsKey (c))
2271 return typename + "." + values [c];
2272 if (valueDef.CustomAttributes.Cast<CustomAttribute> ()
2273 .Any (ca => ca.Constructor.DeclaringType.FullName == "System.FlagsAttribute")) {
2274 return string.Join (" | ",
2275 (from i in values.Keys
2277 select typename + "." + values [i])
2280 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
2283 private static Dictionary<long, string> GetEnumerationValues (TypeDefinition type)
2285 var values = new Dictionary<long, string> ();
2287 (from f in type.Fields.Cast<FieldDefinition> ()
2288 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
2290 values [ToInt64 (f.Constant)] = f.Name;
2295 static long ToInt64 (object value)
2298 return (long) (ulong) value;
2299 return Convert.ToInt64 (value);
2302 private void MakeParameters (XmlElement root, ParameterDefinitionCollection parameters)
2304 XmlElement e = WriteElement(root, "Parameters");
2306 foreach (ParameterDefinition p in parameters) {
2307 XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
2309 pe.SetAttribute("Name", p.Name);
2310 pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
2311 if (p.ParameterType is ReferenceType) {
2312 if (p.IsOut) pe.SetAttribute("RefType", "out");
2313 else pe.SetAttribute("RefType", "ref");
2315 MakeAttributes (pe, p.CustomAttributes, 0);
2319 private void MakeTypeParameters (XmlElement root, GenericParameterCollection typeParams)
2321 if (typeParams == null || typeParams.Count == 0) {
2322 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2324 root.RemoveChild (f);
2327 XmlElement e = WriteElement(root, "TypeParameters");
2329 foreach (GenericParameter t in typeParams) {
2330 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2332 pe.SetAttribute("Name", t.Name);
2333 MakeAttributes (pe, t.CustomAttributes, 0);
2334 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2335 ConstraintCollection constraints = t.Constraints;
2336 GenericParameterAttributes attrs = t.Attributes;
2337 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2345 ce = root.OwnerDocument.CreateElement ("Constraints");
2347 pe.AppendChild (ce);
2348 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2349 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2350 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2351 AppendElementText (ce, "ParameterAttribute", "Covariant");
2352 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2353 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2354 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2355 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2356 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2357 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2358 foreach (TypeReference c in constraints) {
2359 TypeDefinition cd = c.Resolve ();
2360 AppendElementText (ce,
2361 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2362 GetDocTypeFullName (c));
2367 private void MakeParameters (XmlElement root, IMemberReference mi)
2369 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2370 MakeParameters (root, ((MethodDefinition)mi).Parameters);
2371 else if (mi is MethodDefinition) {
2372 MethodDefinition mb = (MethodDefinition) mi;
2373 ParameterDefinitionCollection parameters = mb.Parameters;
2374 MakeParameters(root, parameters);
2375 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2376 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2377 p.SetAttribute ("RefType", "this");
2380 else if (mi is PropertyDefinition) {
2381 ParameterDefinitionCollection parameters = ((PropertyDefinition)mi).Parameters;
2382 if (parameters.Count > 0)
2383 MakeParameters(root, parameters);
2387 else if (mi is FieldDefinition) return;
2388 else if (mi is EventDefinition) return;
2389 else throw new ArgumentException();
2392 private static string GetDocParameterType (TypeReference type)
2394 return GetDocTypeFullName (type).Replace ("@", "&");
2397 private void MakeReturnValue (XmlElement root, TypeReference type, CustomAttributeCollection attributes)
2399 XmlElement e = WriteElement(root, "ReturnValue");
2401 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2402 if (attributes != null)
2403 MakeAttributes(e, attributes, 0);
2406 private void MakeReturnValue (XmlElement root, IMemberReference mi)
2408 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2410 else if (mi is MethodDefinition)
2411 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType.ReturnType, ((MethodDefinition)mi).ReturnType.CustomAttributes);
2412 else if (mi is PropertyDefinition)
2413 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
2414 else if (mi is FieldDefinition)
2415 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
2416 else if (mi is EventDefinition)
2417 MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
2419 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2422 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2424 IMemberReference mi = info.Member;
2425 if (mi is TypeDefinition) return null;
2427 string sigs = MakeMemberSignature(mi);
2428 if (sigs == null) return null; // not publicly visible
2430 // no documentation for property/event accessors. Is there a better way of doing this?
2431 if (mi.Name.StartsWith("get_")) return null;
2432 if (mi.Name.StartsWith("set_")) return null;
2433 if (mi.Name.StartsWith("add_")) return null;
2434 if (mi.Name.StartsWith("remove_")) return null;
2435 if (mi.Name.StartsWith("raise_")) return null;
2437 XmlElement me = doc.CreateElement("Member");
2438 me.SetAttribute("MemberName", GetMemberName (mi));
2442 if (exceptions.HasValue &&
2443 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2444 UpdateExceptions (info.Node, info.Member);
2446 if (since != null) {
2447 XmlNode docs = me.SelectSingleNode("Docs");
2448 docs.AppendChild (CreateSinceNode (doc));
2454 private static string GetMemberName (IMemberReference mi)
2456 MethodDefinition mb = mi as MethodDefinition;
2458 PropertyDefinition pi = mi as PropertyDefinition;
2461 return DocUtils.GetPropertyName (pi);
2463 StringBuilder sb = new StringBuilder (mi.Name.Length);
2464 if (!DocUtils.IsExplicitlyImplemented (mb))
2465 sb.Append (mi.Name);
2467 TypeReference iface;
2468 MethodReference ifaceMethod;
2469 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2470 sb.Append (GetDocTypeFullName (iface));
2472 sb.Append (ifaceMethod.Name);
2474 if (mb.IsGenericMethod ()) {
2475 GenericParameterCollection typeParams = mb.GenericParameters;
2476 if (typeParams.Count > 0) {
2478 sb.Append (typeParams [0].Name);
2479 for (int i = 1; i < typeParams.Count; ++i)
2480 sb.Append (",").Append (typeParams [i].Name);
2484 return sb.ToString ();
2487 private static int CountChars (string s, char c)
2490 for (int i = 0; i < s.Length; ++i) {
2497 /// SIGNATURE GENERATION FUNCTIONS
2499 static string MakeTypeSignature (TypeReference type)
2501 return csharpFormatter.GetDeclaration (type);
2504 static string MakeMemberSignature (IMemberReference mi)
2506 return csharpFullFormatter.GetDeclaration (mi);
2509 static string GetMemberType (IMemberReference mi)
2511 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2512 return "Constructor";
2513 if (mi is MethodDefinition)
2515 if (mi is PropertyDefinition)
2517 if (mi is FieldDefinition)
2519 if (mi is EventDefinition)
2521 throw new ArgumentException();
2524 private static string GetDocTypeName (TypeReference type)
2526 return docTypeFormatter.GetName (type);
2529 private static string GetDocTypeFullName (TypeReference type)
2531 return DocTypeFullMemberFormatter.Default.GetName (type);
2534 class DocsNodeInfo {
2535 public DocsNodeInfo (XmlElement node)
2540 public DocsNodeInfo (XmlElement node, TypeDefinition type)
2546 public DocsNodeInfo (XmlElement node, IMemberReference member)
2549 SetMemberInfo (member);
2552 void SetType (TypeDefinition type)
2555 throw new ArgumentNullException ("type");
2556 GenericParameters = new List<GenericParameter> (type.GenericParameters.Cast<GenericParameter> ());
2557 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
2558 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
2559 for (int i = 0; i < declTypes.Count - 1; ++i) {
2560 int remove = System.Math.Min (maxGenArgs,
2561 DocUtils.GetGenericArgumentCount (declTypes [i]));
2562 maxGenArgs -= remove;
2563 while (remove-- > 0)
2564 GenericParameters.RemoveAt (0);
2566 if (DocUtils.IsDelegate (type)) {
2567 Parameters = type.GetMethod("Invoke").Parameters;
2568 ReturnType = type.GetMethod("Invoke").ReturnType.ReturnType;
2570 SetSlashDocs (type);
2573 void SetMemberInfo (IMemberReference member)
2576 throw new ArgumentNullException ("member");
2577 ReturnIsReturn = true;
2581 if (member is MethodReference ) {
2582 MethodReference mr = (MethodReference) member;
2583 Parameters = mr.Parameters;
2584 if (mr.IsGenericMethod ()) {
2585 GenericParameters = new List<GenericParameter> (mr.GenericParameters.Cast<GenericParameter> ());
2588 else if (member is PropertyDefinition) {
2589 Parameters = ((PropertyDefinition) member).Parameters;
2592 if (member is MethodDefinition) {
2593 ReturnType = ((MethodDefinition) member).ReturnType.ReturnType;
2594 } else if (member is PropertyDefinition) {
2595 ReturnType = ((PropertyDefinition) member).PropertyType;
2596 ReturnIsReturn = false;
2599 // no remarks section for enum members
2600 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
2602 SetSlashDocs (member);
2605 private void SetSlashDocs (IMemberReference member)
2607 if (slashdocs == null)
2610 string slashdocsig = slashdocFormatter.GetDeclaration (member);
2611 if (slashdocsig != null)
2612 SlashDocs = slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
2615 public TypeReference ReturnType;
2616 public List<GenericParameter> GenericParameters;
2617 public ParameterDefinitionCollection Parameters;
2618 public bool ReturnIsReturn;
2619 public XmlElement Node;
2620 public bool AddRemarks = true;
2621 public XmlNode SlashDocs;
2622 public XmlReader EcmaDocs;
2623 public IMemberReference Member;
2626 static string GetXPathForMember (DocumentationMember member)
2628 StringBuilder xpath = new StringBuilder ();
2629 xpath.Append ("//Members/Member[@MemberName=\"")
2630 .Append (member.MemberName)
2632 if (member.Parameters != null && member.Parameters.Count > 0) {
2633 xpath.Append ("/Parameters[count(Parameter) = ")
2634 .Append (member.Parameters.Count);
2635 for (int i = 0; i < member.Parameters.Count; ++i) {
2636 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2637 xpath.Append (member.Parameters [i]);
2638 xpath.Append ("\"");
2640 xpath.Append ("]/..");
2642 return xpath.ToString ();
2645 public static string GetXPathForMember (XPathNavigator member)
2647 StringBuilder xpath = new StringBuilder ();
2648 xpath.Append ("//Type[@FullName=\"")
2649 .Append (member.SelectSingleNode ("../../@FullName").Value)
2651 xpath.Append ("Members/Member[@MemberName=\"")
2652 .Append (member.SelectSingleNode ("@MemberName").Value)
2654 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2655 if (parameters.Count > 0) {
2656 xpath.Append ("/Parameters[count(Parameter) = ")
2657 .Append (parameters.Count);
2659 while (parameters.MoveNext ()) {
2661 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2662 xpath.Append (parameters.Current.Value);
2663 xpath.Append ("\"");
2665 xpath.Append ("]/..");
2667 return xpath.ToString ();
2670 public static string GetXPathForMember (IMemberReference member)
2672 StringBuilder xpath = new StringBuilder ();
2673 xpath.Append ("//Type[@FullName=\"")
2674 .Append (member.DeclaringType.FullName)
2676 xpath.Append ("Members/Member[@MemberName=\"")
2677 .Append (GetMemberName (member))
2680 ParameterDefinitionCollection parameters = null;
2681 if (member is MethodDefinition)
2682 parameters = ((MethodDefinition) member).Parameters;
2683 else if (member is PropertyDefinition) {
2684 parameters = ((PropertyDefinition) member).Parameters;
2686 if (parameters != null && parameters.Count > 0) {
2687 xpath.Append ("/Parameters[count(Parameter) = ")
2688 .Append (parameters.Count);
2689 for (int i = 0; i < parameters.Count; ++i) {
2690 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2691 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2692 xpath.Append ("\"");
2694 xpath.Append ("]/..");
2696 return xpath.ToString ();
2700 static class CecilExtensions {
2701 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type)
2703 foreach (var c in type.Constructors)
2704 yield return (IMemberReference) c;
2705 foreach (var e in type.Events)
2706 yield return (IMemberReference) e;
2707 foreach (var f in type.Fields)
2708 yield return (IMemberReference) f;
2709 foreach (var m in type.Methods)
2710 yield return (IMemberReference) m;
2711 foreach (var t in type.NestedTypes)
2712 yield return (IMemberReference) t;
2713 foreach (var p in type.Properties)
2714 yield return (IMemberReference) p;
2717 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type, string member)
2719 return GetMembers (type).Where (m => m.Name == member);
2722 public static IMemberReference GetMember (this TypeDefinition type, string member)
2724 return GetMembers (type, member).EnsureZeroOrOne ();
2727 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2729 if (source.Count () > 1)
2730 throw new InvalidOperationException ("too many matches");
2731 return source.FirstOrDefault ();
2734 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2736 return type.Methods.Cast<MethodDefinition> ()
2737 .Where (m => m.Name == method)
2738 .EnsureZeroOrOne ();
2741 public static IEnumerable<IMemberReference> GetDefaultMembers (this TypeReference type)
2743 TypeDefinition def = type as TypeDefinition;
2745 return new IMemberReference [0];
2746 CustomAttribute defMemberAttr = type.CustomAttributes.Cast<CustomAttribute> ()
2747 .Where (c => c.Constructor.DeclaringType.FullName == "System.Reflection.DefaultMemberAttribute")
2749 if (defMemberAttr == null)
2750 return new IMemberReference [0];
2751 string name = (string) defMemberAttr.ConstructorParameters [0];
2752 return def.Properties.Cast<PropertyDefinition> ()
2753 .Where (p => p.Name == name)
2754 .Select (p => (IMemberReference) p);
2757 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2759 return assembly.Modules.Cast<ModuleDefinition> ()
2760 .SelectMany (md => md.Types.Cast<TypeDefinition> ());
2763 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2765 return GetTypes (assembly)
2766 .Where (td => td.FullName == type)
2767 .EnsureZeroOrOne ();
2770 public static bool IsGenericType (this TypeReference type)
2772 return type.GenericParameters.Count > 0;
2775 public static bool IsGenericMethod (this MethodReference method)
2777 return method.GenericParameters.Count > 0;
2780 public static IMemberReference Resolve (this IMemberReference member)
2782 EventReference er = member as EventReference;
2784 return er.Resolve ();
2785 FieldReference fr = member as FieldReference;
2787 return fr.Resolve ();
2788 MethodReference mr = member as MethodReference;
2790 return mr.Resolve ();
2791 PropertyReference pr = member as PropertyReference;
2793 return pr.Resolve ();
2794 TypeReference tr = member as TypeReference;
2796 return tr.Resolve ();
2797 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2801 static class DocUtils {
2802 public static bool IsExplicitlyImplemented (MethodDefinition method)
2804 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2807 public static string GetTypeDotMember (string name)
2809 int startType, startMethod;
2810 startType = startMethod = -1;
2811 for (int i = 0; i < name.Length; ++i) {
2812 if (name [i] == '.') {
2813 startType = startMethod;
2817 return name.Substring (startType+1);
2820 public static string GetMember (string name)
2822 int i = name.LastIndexOf ('.');
2825 return name.Substring (i+1);
2828 public static void GetInfoForExplicitlyImplementedMethod (
2829 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2833 if (method.Overrides.Count != 1)
2834 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2835 iface = method.Overrides [0].DeclaringType;
2836 ifaceMethod = method.Overrides [0];
2839 public static string GetPropertyName (PropertyDefinition pi)
2841 // Issue: (g)mcs-generated assemblies that explicitly implement
2842 // properties don't specify the full namespace, just the
2843 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2844 MethodDefinition method = pi.GetMethod;
2846 method = pi.SetMethod;
2847 if (!IsExplicitlyImplemented (method))
2850 // Need to determine appropriate namespace for this member.
2851 TypeReference iface;
2852 MethodReference ifaceMethod;
2853 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2854 return string.Join (".", new string[]{
2855 DocTypeFullMemberFormatter.Default.GetName (iface),
2856 GetMember (pi.Name)});
2859 public static string GetNamespace (TypeReference type)
2861 if (type.GetOriginalType ().IsNested)
2862 type = type.GetOriginalType ();
2863 while (type != null && type.IsNested)
2864 type = type.DeclaringType;
2866 return string.Empty;
2867 return type.Namespace;
2870 public static string PathCombine (string dir, string path)
2876 return Path.Combine (dir, path);
2879 public static bool IsExtensionMethod (MethodDefinition method)
2882 method.CustomAttributes.Cast<CustomAttribute> ()
2883 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2885 method.DeclaringType.CustomAttributes.Cast<CustomAttribute> ()
2886 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2890 public static bool IsDelegate (TypeDefinition type)
2892 TypeReference baseRef = type.BaseType;
2893 if (baseRef == null)
2895 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2896 baseRef.FullName == "System.MulticastDelegate";
2899 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2901 List<TypeReference> decls = new List<TypeReference> ();
2903 while (type.DeclaringType != null) {
2904 decls.Add (type.DeclaringType);
2905 type = type.DeclaringType;
2911 public static int GetGenericArgumentCount (TypeReference type)
2913 GenericInstanceType inst = type as GenericInstanceType;
2915 ? inst.GenericArguments.Count
2916 : type.GenericParameters.Count;
2919 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
2921 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
2922 List<TypeReference> userInterfaces = new List<TypeReference> ();
2923 foreach (TypeReference iface in type.Interfaces) {
2924 TypeReference lookup = iface.Resolve () ?? iface;
2925 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
2926 userInterfaces.Add (iface);
2928 return userInterfaces;
2931 private static string GetQualifiedTypeName (TypeReference type)
2933 return "[" + type.Scope.Name + "]" + type.FullName;
2936 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
2938 HashSet<string> inheritedInterfaces = new HashSet<string> ();
2939 Action<TypeDefinition> a = null;
2941 if (t == null) return;
2942 foreach (TypeReference r in t.Interfaces) {
2943 inheritedInterfaces.Add (GetQualifiedTypeName (r));
2947 TypeReference baseRef = type.BaseType;
2948 while (baseRef != null) {
2949 TypeDefinition baseDef = baseRef.Resolve ();
2950 if (baseDef != null) {
2952 baseRef = baseDef.BaseType;
2957 foreach (TypeReference r in type.Interfaces)
2959 return inheritedInterfaces;
2963 class DocumentationMember {
2964 public StringToStringMap MemberSignatures = new StringToStringMap ();
2965 public string ReturnType;
2966 public StringList Parameters;
2967 public string MemberName;
2968 public string MemberType;
2970 public DocumentationMember (XmlReader reader)
2972 MemberName = reader.GetAttribute ("MemberName");
2973 int depth = reader.Depth;
2975 StringList p = new StringList ();
2977 if (reader.NodeType != XmlNodeType.Element)
2979 switch (reader.Name) {
2980 case "MemberSignature":
2981 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
2984 MemberType = reader.ReadElementString ();
2987 if (reader.Depth == depth + 2)
2988 ReturnType = reader.ReadElementString ();
2991 if (reader.Depth == depth + 2)
2992 p.Add (reader.GetAttribute ("Type"));
2995 if (reader.Depth == depth + 1)
2999 } while (go && reader.Read () && reader.Depth >= depth);
3005 public DocumentationMember (XmlNode node)
3007 MemberName = node.Attributes ["MemberName"].Value;
3008 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3009 XmlAttribute l = n.Attributes ["Language"];
3010 XmlAttribute v = n.Attributes ["Value"];
3011 if (l != null && v != null)
3012 MemberSignatures [l.Value] = v.Value;
3014 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3015 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
3017 ReturnType = rt.InnerText;
3018 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
3020 Parameters = new StringList (p.Count);
3021 for (int i = 0; i < p.Count; ++i)
3022 Parameters.Add (p [i].Attributes ["Type"].Value);
3027 public enum MemberFormatterState {
3030 WithinGenericTypeContainer,
3033 public abstract class MemberFormatter {
3034 public virtual string GetName (IMemberReference member)
3036 TypeReference type = member as TypeReference;
3038 return GetTypeName (type);
3039 MethodReference method = member as MethodReference;
3040 if (method != null && method.Name == ".ctor") // method.IsConstructor
3041 return GetConstructorName (method);
3043 return GetMethodName (method);
3044 PropertyReference prop = member as PropertyReference;
3046 return GetPropertyName (prop);
3047 FieldReference field = member as FieldReference;
3049 return GetFieldName (field);
3050 EventReference e = member as EventReference;
3052 return GetEventName (e);
3053 throw new NotSupportedException ("Can't handle: " +
3054 (member == null ? "null" : member.GetType().ToString()));
3057 protected virtual string GetTypeName (TypeReference type)
3060 throw new ArgumentNullException ("type");
3061 return _AppendTypeName (new StringBuilder (type.Name.Length), type).ToString ();
3064 protected virtual char[] ArrayDelimeters {
3065 get {return new char[]{'[', ']'};}
3068 protected virtual MemberFormatterState MemberFormatterState { get; set; }
3070 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type)
3072 if (type is ArrayType) {
3073 TypeSpecification spec = type as TypeSpecification;
3074 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3075 .Append (ArrayDelimeters [0]);
3076 var origState = MemberFormatterState;
3077 MemberFormatterState = MemberFormatterState.WithinArray;
3078 ArrayType array = (ArrayType) type;
3079 int rank = array.Rank;
3081 buf.Append (new string (',', rank-1));
3082 MemberFormatterState = origState;
3083 return buf.Append (ArrayDelimeters [1]);
3085 if (type is ReferenceType) {
3086 return AppendRefTypeName (buf, type);
3088 if (type is PointerType) {
3089 return AppendPointerTypeName (buf, type);
3091 AppendNamespace (buf, type);
3092 if (type is GenericParameter) {
3093 return AppendTypeName (buf, type);
3095 GenericInstanceType genInst = type as GenericInstanceType;
3096 if (type.GenericParameters.Count == 0 &&
3097 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3098 return AppendFullTypeName (buf, type);
3100 return AppendGenericType (buf, type);
3103 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3105 string ns = DocUtils.GetNamespace (type);
3106 if (ns != null && ns.Length > 0)
3107 buf.Append (ns).Append ('.');
3111 private StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type)
3113 if (type.DeclaringType != null)
3114 AppendFullTypeName (buf, type.DeclaringType).Append (NestedTypeSeparator);
3115 return AppendTypeName (buf, type);
3118 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3120 return AppendTypeName (buf, type.Name);
3123 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3125 int n = typename.IndexOf ("`");
3127 return buf.Append (typename.Substring (0, n));
3128 return buf.Append (typename);
3131 protected virtual string RefTypeModifier {
3135 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type)
3137 TypeSpecification spec = type as TypeSpecification;
3138 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3139 .Append (RefTypeModifier);
3142 protected virtual string PointerModifier {
3146 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type)
3148 TypeSpecification spec = type as TypeSpecification;
3149 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3150 .Append (PointerModifier);
3153 protected virtual char[] GenericTypeContainer {
3154 get {return new char[]{'<', '>'};}
3157 protected virtual char NestedTypeSeparator {
3161 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3163 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3164 type is GenericInstanceType ? type.GetOriginalType () : type);
3165 List<TypeReference> genArgs = GetGenericArguments (type);
3168 bool insertNested = false;
3169 foreach (var decl in decls) {
3170 TypeReference declDef = decl.Resolve () ?? decl;
3172 buf.Append (NestedTypeSeparator);
3174 insertNested = true;
3175 AppendTypeName (buf, declDef);
3176 int ac = DocUtils.GetGenericArgumentCount (declDef);
3180 buf.Append (GenericTypeContainer [0]);
3181 var origState = MemberFormatterState;
3182 MemberFormatterState = MemberFormatterState.WithinGenericTypeContainer;
3183 _AppendTypeName (buf, genArgs [argIdx++]);
3184 for (int i = 1; i < c; ++i)
3185 _AppendTypeName (buf.Append (","), genArgs [argIdx++]);
3186 MemberFormatterState = origState;
3187 buf.Append (GenericTypeContainer [1]);
3193 private List<TypeReference> GetGenericArguments (TypeReference type)
3195 var args = new List<TypeReference> ();
3196 GenericInstanceType inst = type as GenericInstanceType;
3198 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
3200 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
3204 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3209 protected virtual string GetConstructorName (MethodReference constructor)
3211 return constructor.Name;
3214 protected virtual string GetMethodName (MethodReference method)
3219 protected virtual string GetPropertyName (PropertyReference property)
3221 return property.Name;
3224 protected virtual string GetFieldName (FieldReference field)
3229 protected virtual string GetEventName (EventReference e)
3234 public virtual string GetDeclaration (IMemberReference member)
3237 throw new ArgumentNullException ("member");
3238 TypeDefinition type = member as TypeDefinition;
3240 return GetTypeDeclaration (type);
3241 MethodDefinition method = member as MethodDefinition;
3242 if (method != null && method.IsConstructor)
3243 return GetConstructorDeclaration (method);
3245 return GetMethodDeclaration (method);
3246 PropertyDefinition prop = member as PropertyDefinition;
3248 return GetPropertyDeclaration (prop);
3249 FieldDefinition field = member as FieldDefinition;
3251 return GetFieldDeclaration (field);
3252 EventDefinition e = member as EventDefinition;
3254 return GetEventDeclaration (e);
3255 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3258 protected virtual string GetTypeDeclaration (TypeDefinition type)
3261 throw new ArgumentNullException ("type");
3262 StringBuilder buf = new StringBuilder (type.Name.Length);
3263 _AppendTypeName (buf, type);
3264 AppendGenericTypeConstraints (buf, type);
3265 return buf.ToString ();
3268 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
3270 return GetConstructorName (constructor);
3273 protected virtual string GetMethodDeclaration (MethodDefinition method)
3275 // Special signature for destructors.
3276 if (method.Name == "Finalize" && method.Parameters.Count == 0)
3277 return GetFinalizerName (method);
3279 StringBuilder buf = new StringBuilder ();
3281 AppendVisibility (buf, method);
3282 if (buf.Length == 0 &&
3283 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3286 AppendModifiers (buf, method);
3288 if (buf.Length != 0)
3290 buf.Append (GetName (method.ReturnType.ReturnType)).Append (" ");
3292 AppendMethodName (buf, method);
3293 AppendGenericMethod (buf, method).Append (" ");
3294 AppendParameters (buf, method, method.Parameters);
3295 AppendGenericMethodConstraints (buf, method);
3296 return buf.ToString ();
3299 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3301 return buf.Append (method.Name);
3304 protected virtual string GetFinalizerName (MethodDefinition method)
3309 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3314 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3319 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3324 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3329 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3334 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
3336 return GetPropertyName (property);
3339 protected virtual string GetFieldDeclaration (FieldDefinition field)
3341 return GetFieldName (field);
3344 protected virtual string GetEventDeclaration (EventDefinition e)
3346 return GetEventName (e);
3350 class CSharpFullMemberFormatter : MemberFormatter {
3352 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3354 string ns = DocUtils.GetNamespace (type);
3355 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
3356 buf.Append (ns).Append ('.');
3360 private string GetCSharpType (string t)
3363 case "System.Byte": return "byte";
3364 case "System.SByte": return "sbyte";
3365 case "System.Int16": return "short";
3366 case "System.Int32": return "int";
3367 case "System.Int64": return "long";
3369 case "System.UInt16": return "ushort";
3370 case "System.UInt32": return "uint";
3371 case "System.UInt64": return "ulong";
3373 case "System.Single": return "float";
3374 case "System.Double": return "double";
3375 case "System.Decimal": return "decimal";
3376 case "System.Boolean": return "bool";
3377 case "System.Char": return "char";
3378 case "System.Void": return "void";
3379 case "System.String": return "string";
3380 case "System.Object": return "object";
3385 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3387 if (type is GenericParameter)
3388 return AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
3389 string t = type.FullName;
3390 if (!t.StartsWith ("System.")) {
3391 return base.AppendTypeName (buf, type);
3394 string s = GetCSharpType (t);
3396 return buf.Append (s);
3398 return base.AppendTypeName (buf, type);
3401 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
3403 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeContainer)
3405 GenericParameterAttributes attrs = type.Attributes;
3406 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
3407 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
3411 buf.Append ("out ");
3415 protected override string GetTypeDeclaration (TypeDefinition type)
3417 string visibility = GetTypeVisibility (type.Attributes);
3418 if (visibility == null)
3421 StringBuilder buf = new StringBuilder ();
3423 buf.Append (visibility);
3426 MemberFormatter full = new CSharpFullMemberFormatter ();
3428 if (DocUtils.IsDelegate (type)) {
3429 buf.Append("delegate ");
3430 MethodDefinition invoke = type.GetMethod ("Invoke");
3431 buf.Append (full.GetName (invoke.ReturnType.ReturnType)).Append (" ");
3432 buf.Append (GetName (type));
3433 AppendParameters (buf, invoke, invoke.Parameters);
3434 AppendGenericTypeConstraints (buf, type);
3437 return buf.ToString();
3440 if (type.IsAbstract && !type.IsInterface)
3441 buf.Append("abstract ");
3442 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
3443 buf.Append("sealed ");
3444 buf.Replace ("abstract sealed", "static");
3446 buf.Append (GetTypeKind (type));
3448 buf.Append (GetCSharpType (type.FullName) == null
3453 TypeReference basetype = type.BaseType;
3454 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
3457 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
3458 .Select (iface => full.GetName (iface))
3462 if (basetype != null || interface_names.Count > 0)
3465 if (basetype != null) {
3466 buf.Append (full.GetName (basetype));
3467 if (interface_names.Count > 0)
3471 for (int i = 0; i < interface_names.Count; i++){
3474 buf.Append (interface_names [i]);
3476 AppendGenericTypeConstraints (buf, type);
3479 return buf.ToString ();
3482 static string GetTypeKind (TypeDefinition t)
3488 if (t.IsClass || t.FullName == "System.Enum")
3492 throw new ArgumentException(t.FullName);
3495 static string GetTypeVisibility (TypeAttributes ta)
3497 switch (ta & TypeAttributes.VisibilityMask) {
3498 case TypeAttributes.Public:
3499 case TypeAttributes.NestedPublic:
3502 case TypeAttributes.NestedFamily:
3503 case TypeAttributes.NestedFamORAssem:
3511 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3513 if (type.GenericParameters.Count == 0)
3515 return AppendConstraints (buf, type.GenericParameters);
3518 private StringBuilder AppendConstraints (StringBuilder buf, GenericParameterCollection genArgs)
3520 foreach (GenericParameter genArg in genArgs) {
3521 GenericParameterAttributes attrs = genArg.Attributes;
3522 ConstraintCollection constraints = genArg.Constraints;
3523 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
3526 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
3527 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
3528 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
3531 if (!isref && !isvt && !isnew && constraints.Count == 0)
3533 buf.Append (" where ").Append (genArg.Name).Append (" : ");
3535 buf.Append ("class");
3539 buf.Append ("struct");
3542 if (constraints.Count > 0 && !isvt) {
3545 buf.Append (GetTypeName (constraints [0]));
3546 for (int i = 1; i < constraints.Count; ++i)
3547 buf.Append (", ").Append (GetTypeName (constraints [i]));
3549 if (isnew && !isvt) {
3552 buf.Append ("new()");
3558 protected override string GetConstructorDeclaration (MethodDefinition constructor)
3560 StringBuilder buf = new StringBuilder ();
3561 AppendVisibility (buf, constructor);
3562 if (buf.Length == 0)
3566 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
3567 AppendParameters (buf, constructor, constructor.Parameters);
3570 return buf.ToString ();
3573 protected override string GetMethodDeclaration (MethodDefinition method)
3575 string decl = base.GetMethodDeclaration (method);
3581 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3583 if (DocUtils.IsExplicitlyImplemented (method)) {
3584 TypeReference iface;
3585 MethodReference ifaceMethod;
3586 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3587 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3589 .Append (ifaceMethod.Name);
3591 return base.AppendMethodName (buf, method);
3594 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3596 if (method.GenericParameters.Count == 0)
3598 return AppendConstraints (buf, method.GenericParameters);
3601 protected override string RefTypeModifier {
3605 protected override string GetFinalizerName (MethodDefinition method)
3607 return "~" + method.DeclaringType.Name + " ()";
3610 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3614 if (method.IsPublic)
3615 return buf.Append ("public");
3616 if (method.IsFamily || method.IsFamilyOrAssembly)
3617 return buf.Append ("protected");
3621 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3623 string modifiers = String.Empty;
3624 if (method.IsStatic) modifiers += " static";
3625 if (method.IsVirtual && !method.IsAbstract) {
3626 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3627 else modifiers += " override";
3629 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
3630 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
3631 if (method.IsFinal) modifiers += " sealed";
3632 if (modifiers == " virtual sealed") modifiers = "";
3634 return buf.Append (modifiers);
3637 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3639 if (method.IsGenericMethod ()) {
3640 GenericParameterCollection args = method.GenericParameters;
3641 if (args.Count > 0) {
3643 buf.Append (args [0].Name);
3644 for (int i = 1; i < args.Count; ++i)
3645 buf.Append (",").Append (args [i].Name);
3652 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3654 return AppendParameters (buf, method, parameters, '(', ')');
3657 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters, char begin, char end)
3661 if (parameters.Count > 0) {
3662 if (DocUtils.IsExtensionMethod (method))
3663 buf.Append ("this ");
3664 AppendParameter (buf, parameters [0]);
3665 for (int i = 1; i < parameters.Count; ++i) {
3667 AppendParameter (buf, parameters [i]);
3671 return buf.Append (end);
3674 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
3676 if (parameter.ParameterType is ReferenceType) {
3677 if (parameter.IsOut)
3678 buf.Append ("out ");
3680 buf.Append ("ref ");
3682 buf.Append (GetName (parameter.ParameterType)).Append (" ");
3683 return buf.Append (parameter.Name);
3686 protected override string GetPropertyDeclaration (PropertyDefinition property)
3688 MethodDefinition method;
3690 string get_visible = null;
3691 if ((method = property.GetMethod) != null &&
3692 (DocUtils.IsExplicitlyImplemented (method) ||
3693 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3694 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3695 string set_visible = null;
3696 if ((method = property.SetMethod) != null &&
3697 (DocUtils.IsExplicitlyImplemented (method) ||
3698 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3699 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3701 if ((set_visible == null) && (get_visible == null))
3705 StringBuilder buf = new StringBuilder ();
3706 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
3707 buf.Append (visibility = get_visible);
3708 else if (set_visible != null && get_visible == null)
3709 buf.Append (visibility = set_visible);
3711 buf.Append (visibility = "public");
3713 // Pick an accessor to use for static/virtual/override/etc. checks.
3714 method = property.SetMethod;
3716 method = property.GetMethod;
3718 string modifiers = String.Empty;
3719 if (method.IsStatic) modifiers += " static";
3720 if (method.IsVirtual && !method.IsAbstract) {
3721 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
3722 modifiers += " virtual";
3724 modifiers += " override";
3726 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
3727 if (method.IsAbstract && !declDef.IsInterface)
3728 modifiers += " abstract";
3730 modifiers += " sealed";
3731 if (modifiers == " virtual sealed")
3733 buf.Append (modifiers).Append (' ');
3735 buf.Append (GetName (property.PropertyType)).Append (' ');
3737 IEnumerable<IMemberReference> defs = property.DeclaringType.GetDefaultMembers ();
3738 string name = property.Name;
3739 foreach (IMemberReference mi in defs) {
3740 if (mi == property) {
3745 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
3747 if (property.Parameters.Count != 0) {
3748 AppendParameters (buf, method, property.Parameters, '[', ']');
3752 if (set_visible != null) {
3753 if (set_visible != visibility)
3754 buf.Append (' ').Append (set_visible);
3755 buf.Append (" set;");
3757 if (get_visible != null) {
3758 if (get_visible != visibility)
3759 buf.Append (' ').Append (get_visible);
3760 buf.Append (" get;");
3764 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
3767 protected override string GetFieldDeclaration (FieldDefinition field)
3769 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
3770 if (declType.IsEnum && field.Name == "value__")
3771 return null; // This member of enums aren't documented.
3773 StringBuilder buf = new StringBuilder ();
3774 AppendFieldVisibility (buf, field);
3775 if (buf.Length == 0)
3778 if (declType.IsEnum)
3781 if (field.IsStatic && !field.IsLiteral)
3782 buf.Append (" static");
3783 if (field.IsInitOnly)
3784 buf.Append (" readonly");
3785 if (field.IsLiteral)
3786 buf.Append (" const");
3788 buf.Append (' ').Append (GetName (field.FieldType)).Append (' ');
3789 buf.Append (field.Name);
3790 AppendFieldValue (buf, field);
3793 return buf.ToString ();
3796 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
3799 return buf.Append ("public");
3800 if (field.IsFamily || field.IsFamilyOrAssembly)
3801 return buf.Append ("protected");
3805 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
3807 // enums have a value__ field, which we ignore
3808 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
3809 field.DeclaringType.IsGenericType ())
3811 if (field.HasConstant && field.IsLiteral) {
3814 val = field.Constant;
3819 buf.Append (" = ").Append ("null");
3820 else if (val is Enum)
3821 buf.Append (" = ").Append (val.ToString ());
3822 else if (val is IFormattable) {
3823 string value = ((IFormattable)val).ToString();
3825 value = "\"" + value + "\"";
3826 buf.Append (" = ").Append (value);
3832 protected override string GetEventDeclaration (EventDefinition e)
3834 StringBuilder buf = new StringBuilder ();
3835 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
3839 AppendModifiers (buf, e.AddMethod);
3841 buf.Append (" event ");
3842 buf.Append (GetName (e.EventType)).Append (' ');
3843 buf.Append (e.Name).Append (';');
3845 return buf.ToString ();
3849 class CSharpMemberFormatter : CSharpFullMemberFormatter {
3850 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3856 class DocTypeFullMemberFormatter : MemberFormatter {
3857 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
3859 protected override char NestedTypeSeparator {
3864 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
3865 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3871 class SlashDocMemberFormatter : MemberFormatter {
3873 protected override char[] GenericTypeContainer {
3874 get {return new char[]{'{', '}'};}
3877 private bool AddTypeCount = true;
3879 private TypeReference genDeclType;
3880 private MethodReference genDeclMethod;
3882 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3884 if (type is GenericParameter) {
3886 if (genDeclType != null) {
3887 GenericParameterCollection genArgs = genDeclType.GenericParameters;
3888 for (int i = 0; i < genArgs.Count; ++i) {
3889 if (genArgs [i].Name == type.Name) {
3890 buf.Append ('`').Append (i);
3895 if (genDeclMethod != null) {
3896 GenericParameterCollection genArgs = null;
3897 if (genDeclMethod.IsGenericMethod ()) {
3898 genArgs = genDeclMethod.GenericParameters;
3899 for (int i = 0; i < genArgs.Count; ++i) {
3900 if (genArgs [i].Name == type.Name) {
3901 buf.Append ("``").Append (i);
3907 if (genDeclType == null && genDeclMethod == null) {
3908 // Probably from within an explicitly implemented interface member,
3909 // where CSC uses parameter names instead of indices (why?), e.g.
3910 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
3911 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
3912 buf.Append (type.Name);
3914 if (buf.Length == l) {
3915 throw new Exception (string.Format (
3916 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
3917 type.Name, genDeclType, genDeclMethod));
3921 base.AppendTypeName (buf, type);
3923 int numArgs = type.GenericParameters.Count;
3924 if (type.DeclaringType != null)
3925 numArgs -= type.GenericParameters.Count;
3927 buf.Append ('`').Append (numArgs);
3934 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3937 base.AppendGenericType (buf, type);
3939 AppendType (buf, type);
3943 private StringBuilder AppendType (StringBuilder buf, TypeReference type)
3945 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
3946 bool insertNested = false;
3947 int prevParamCount = 0;
3948 foreach (var decl in decls) {
3950 buf.Append (NestedTypeSeparator);
3951 insertNested = true;
3952 base.AppendTypeName (buf, decl);
3953 int argCount = DocUtils.GetGenericArgumentCount (decl);
3954 int numArgs = argCount - prevParamCount;
3955 prevParamCount = argCount;
3957 buf.Append ('`').Append (numArgs);
3962 public override string GetDeclaration (IMemberReference member)
3964 TypeReference r = member as TypeReference;
3966 return "T:" + GetTypeName (r);
3968 return base.GetDeclaration (member);
3971 protected override string GetConstructorName (MethodReference constructor)
3973 return GetMethodDefinitionName (constructor, "#ctor");
3976 protected override string GetMethodName (MethodReference method)
3979 MethodDefinition methodDef = method as MethodDefinition;
3980 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
3983 TypeReference iface;
3984 MethodReference ifaceMethod;
3985 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
3986 AddTypeCount = false;
3987 name = GetTypeName (iface) + "." + ifaceMethod.Name;
3988 AddTypeCount = true;
3990 return GetMethodDefinitionName (method, name);
3993 private string GetMethodDefinitionName (MethodReference method, string name)
3995 StringBuilder buf = new StringBuilder ();
3996 buf.Append (GetTypeName (method.DeclaringType));
3998 buf.Append (name.Replace (".", "#"));
3999 if (method.IsGenericMethod ()) {
4000 GenericParameterCollection genArgs = method.GenericParameters;
4001 if (genArgs.Count > 0)
4002 buf.Append ("``").Append (genArgs.Count);
4004 ParameterDefinitionCollection parameters = method.Parameters;
4006 genDeclType = method.DeclaringType;
4007 genDeclMethod = method;
4008 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
4012 genDeclMethod = null;
4014 return buf.ToString ();
4017 private StringBuilder AppendParameters (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinitionCollection parameters)
4019 if (parameters.Count == 0)
4024 AppendParameter (buf, genArgs, parameters [0]);
4025 for (int i = 1; i < parameters.Count; ++i) {
4027 AppendParameter (buf, genArgs, parameters [i]);
4030 return buf.Append (')');
4033 private StringBuilder AppendParameter (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinition parameter)
4035 AddTypeCount = false;
4036 buf.Append (GetTypeName (parameter.ParameterType));
4037 AddTypeCount = true;
4041 protected override string GetPropertyName (PropertyReference property)
4045 PropertyDefinition propertyDef = property as PropertyDefinition;
4046 MethodDefinition method = null;
4047 if (propertyDef != null)
4048 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
4049 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
4050 name = property.Name;
4052 TypeReference iface;
4053 MethodReference ifaceMethod;
4054 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4055 AddTypeCount = false;
4056 name = string.Join ("#", new string[]{
4057 GetTypeName (iface).Replace (".", "#"),
4058 DocUtils.GetMember (property.Name)
4060 AddTypeCount = true;
4063 StringBuilder buf = new StringBuilder ();
4064 buf.Append (GetName (property.DeclaringType));
4067 ParameterDefinitionCollection parameters = property.Parameters;
4068 if (parameters.Count > 0) {
4069 genDeclType = property.DeclaringType;
4071 GenericParameterCollection genArgs = property.DeclaringType.GenericParameters;
4072 AppendParameter (buf, genArgs, parameters [0]);
4073 for (int i = 1; i < parameters.Count; ++i) {
4075 AppendParameter (buf, genArgs, parameters [i]);
4080 return buf.ToString ();
4083 protected override string GetFieldName (FieldReference field)
4085 return string.Format ("{0}.{1}",
4086 GetName (field.DeclaringType), field.Name);
4089 protected override string GetEventName (EventReference e)
4091 return string.Format ("{0}.{1}",
4092 GetName (e.DeclaringType), e.Name);
4095 protected override string GetTypeDeclaration (TypeDefinition type)
4097 string name = GetName (type);
4103 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4105 string name = GetName (constructor);
4111 protected override string GetMethodDeclaration (MethodDefinition method)
4113 string name = GetName (method);
4116 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4117 genDeclType = method.DeclaringType;
4118 genDeclMethod = method;
4119 name += "~" + GetName (method.ReturnType.ReturnType);
4121 genDeclMethod = null;
4126 protected override string GetPropertyDeclaration (PropertyDefinition property)
4128 string name = GetName (property);
4134 protected override string GetFieldDeclaration (FieldDefinition field)
4136 string name = GetName (field);
4142 protected override string GetEventDeclaration (EventDefinition e)
4144 string name = GetName (e);
4151 class FileNameMemberFormatter : SlashDocMemberFormatter {
4152 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4157 protected override char NestedTypeSeparator {