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, GetCustomAttributes (assembly.CustomAttributes, ""));
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, GetCustomAttributes (type));
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 MakeAttributes (me, GetCustomAttributes (mi));
1520 MakeReturnValue(me, mi);
1521 if (mi is MethodReference) {
1522 MethodReference mb = (MethodReference) mi;
1523 if (mb.IsGenericMethod ())
1524 MakeTypeParameters (me, mb.GenericParameters);
1526 MakeParameters(me, mi);
1529 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1530 WriteElementText(me, "MemberValue", fieldValue);
1532 info.Node = WriteElement (me, "Docs");
1534 UpdateExtensionMethods (me, info);
1537 IEnumerable<string> GetCustomAttributes (IMemberReference mi)
1539 IEnumerable<string> attrs = Enumerable.Empty<string>();
1541 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1543 attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1545 PropertyReference pr = mi as PropertyReference;
1547 PropertyDefinition pd = pr.Resolve ();
1548 if (pd.GetMethod != null)
1549 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1550 if (pd.SetMethod != null)
1551 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1554 EventReference er = mi as EventReference;
1556 EventDefinition ed = er.Resolve ();
1557 if (ed.AddMethod != null)
1558 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1559 if (ed.RemoveMethod != null)
1560 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1566 IEnumerable<string> GetCustomAttributes (CustomAttributeCollection attributes, string prefix)
1568 foreach (CustomAttribute attribute in attributes.Cast<CustomAttribute> ()
1569 .OrderBy (ca => ca.Constructor.DeclaringType.FullName)) {
1570 if (!attribute.Resolve ()) {
1572 Warning ("warning: could not resolve type {0}.",
1573 attribute.Constructor.DeclaringType.FullName);
1575 TypeDefinition attrType = attribute.Constructor.DeclaringType as TypeDefinition;
1576 if (attrType != null && !IsPublic (attrType))
1578 if (slashdocFormatter.GetName (attribute.Constructor.DeclaringType) == null)
1581 if (Array.IndexOf (IgnorableAttributes, attribute.Constructor.DeclaringType.FullName) >= 0)
1584 StringList fields = new StringList ();
1586 ParameterDefinitionCollection parameters = attribute.Constructor.Parameters;
1587 for (int i = 0; i < attribute.ConstructorParameters.Count; ++i) {
1588 fields.Add (MakeAttributesValueString (
1589 attribute.ConstructorParameters [i],
1590 parameters [i].ParameterType));
1593 (from de in attribute.Fields.Cast<DictionaryEntry> ()
1594 select new { Type=attribute.GetFieldType (de.Key.ToString ()), Name=de.Key, Value=de.Value })
1596 (from de in attribute.Properties.Cast<DictionaryEntry> ()
1597 select new { Type=attribute.GetPropertyType (de.Key.ToString ()), Name=de.Key, Value=de.Value }))
1598 .OrderBy (v => v.Name);
1599 foreach (var d in namedArgs)
1600 fields.Add (string.Format ("{0}={1}", d.Name,
1601 MakeAttributesValueString (d.Value, d.Type)));
1603 string a2 = String.Join(", ", fields.ToArray ());
1604 if (a2 != "") a2 = "(" + a2 + ")";
1606 string name = attribute.Constructor.DeclaringType.FullName;
1607 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1608 yield return prefix + name + a2;
1612 static readonly string[] ValidExtensionMembers = {
1621 static readonly string[] ValidExtensionDocMembers = {
1627 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1629 MethodDefinition me = info.Member as MethodDefinition;
1632 if (info.Parameters.Count < 1)
1634 if (!DocUtils.IsExtensionMethod (me))
1637 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1638 XmlNode member = e.CloneNode (true);
1639 em.AppendChild (member);
1640 RemoveExcept (member, ValidExtensionMembers);
1641 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1642 WriteElementText (member, "MemberType", "ExtensionMethod");
1643 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1644 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1645 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1646 member.AppendChild (link);
1647 AddTargets (em, info);
1649 extensionMethods.Add (em);
1652 private static void RemoveExcept (XmlNode node, string[] except)
1656 MyXmlNodeList remove = null;
1657 foreach (XmlNode n in node.ChildNodes) {
1658 if (Array.BinarySearch (except, n.Name) < 0) {
1660 remove = new MyXmlNodeList ();
1665 foreach (XmlNode n in remove)
1666 node.RemoveChild (n);
1669 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1671 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1672 member.PrependChild (targets);
1673 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1674 AppendElementAttributeText (targets, "Target", "Type",
1675 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1678 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1679 ConstraintCollection constraints = gp.Constraints;
1680 if (constraints.Count == 0)
1681 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1683 foreach (TypeReference c in constraints)
1684 AppendElementAttributeText(targets, "Target", "Type",
1685 slashdocFormatter.GetDeclaration (c));
1689 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1692 TypeDefinition type = field.DeclaringType.Resolve ();
1693 if (type != null && type.IsEnum) return false;
1695 if (type != null && type.IsGenericType ()) return false;
1696 if (!field.HasConstant)
1698 if (field.IsLiteral) {
1699 object val = field.Constant;
1700 if (val == null) value = "null";
1701 else if (val is Enum) value = val.ToString();
1702 else if (val is IFormattable) {
1703 value = ((IFormattable)val).ToString();
1705 value = "\"" + value + "\"";
1707 if (value != null && value != "")
1713 // XML HELPER FUNCTIONS
1715 private static XmlElement WriteElement(XmlNode parent, string element) {
1716 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1718 string[] path = element.Split('/');
1719 foreach (string p in path) {
1720 ret = (XmlElement)parent.SelectSingleNode(p);
1723 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1724 ename = ename.Substring(0, ename.IndexOf('['));
1725 ret = parent.OwnerDocument.CreateElement(ename);
1726 parent.AppendChild(ret);
1735 private static void WriteElementText(XmlNode parent, string element, string value) {
1736 XmlElement node = WriteElement(parent, element);
1737 node.InnerText = value;
1740 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1742 XmlElement n = parent.OwnerDocument.CreateElement (element);
1743 parent.AppendChild (n);
1744 n.InnerText = value;
1748 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1750 XmlElement n = parent.OwnerDocument.CreateElement (element);
1751 parent.AppendChild (n);
1752 n.SetAttribute (attribute, value);
1756 private static XmlNode CopyNode (XmlNode source, XmlNode dest)
1758 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1759 dest.AppendChild (copy);
1763 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1764 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1767 node = WriteElement(parent, element);
1768 node.InnerText = value;
1770 private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
1771 XmlElement node = WriteElement(parent, element);
1772 if (node.GetAttribute(attribute) == value) return;
1773 node.SetAttribute(attribute, value);
1775 private static void ClearElement(XmlElement parent, string name) {
1776 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1778 parent.RemoveChild(node);
1781 // DOCUMENTATION HELPER FUNCTIONS
1783 private void MakeDocNode (DocsNodeInfo info)
1785 List<GenericParameter> genericParams = info.GenericParameters;
1786 ParameterDefinitionCollection parameters = info.Parameters;
1787 TypeReference returntype = info.ReturnType;
1788 bool returnisreturn = info.ReturnIsReturn;
1789 XmlElement e = info.Node;
1790 bool addremarks = info.AddRemarks;
1792 WriteElementInitialText(e, "summary", "To be added.");
1794 if (parameters != null) {
1795 string[] values = new string [parameters.Count];
1796 for (int i = 0; i < values.Length; ++i)
1797 values [i] = parameters [i].Name;
1798 UpdateParameters (e, "param", values);
1801 if (genericParams != null) {
1802 string[] values = new string [genericParams.Count];
1803 for (int i = 0; i < values.Length; ++i)
1804 values [i] = genericParams [i].Name;
1805 UpdateParameters (e, "typeparam", values);
1808 string retnodename = null;
1809 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
1810 retnodename = returnisreturn ? "returns" : "value";
1811 string retnodename_other = !returnisreturn ? "returns" : "value";
1813 // If it has a returns node instead of a value node, change its name.
1814 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1815 if (retother != null) {
1816 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1817 foreach (XmlNode node in retother)
1818 retnode.AppendChild(node.CloneNode(true));
1819 e.ReplaceChild(retnode, retother);
1821 WriteElementInitialText(e, retnodename, "To be added.");
1824 ClearElement(e, "returns");
1825 ClearElement(e, "value");
1829 WriteElementInitialText(e, "remarks", "To be added.");
1831 if (exceptions.HasValue && info.Member != null &&
1832 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
1833 UpdateExceptions (e, info.Member);
1836 if (info.EcmaDocs != null) {
1837 XmlReader r = info.EcmaDocs;
1838 int depth = r.Depth;
1839 r.ReadStartElement ("Docs");
1841 if (r.Name == "Docs") {
1842 if (r.Depth == depth && r.NodeType == XmlNodeType.EndElement)
1845 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
1847 if (!r.IsStartElement ())
1852 string name = r.GetAttribute ("name");
1855 XmlNode doc = e.SelectSingleNode (
1856 r.Name + "[@name='" + name + "']");
1857 string value = r.ReadInnerXml ();
1859 doc.InnerXml = value.Replace ("\r", "");
1866 string name = r.Name;
1867 string cref = r.GetAttribute ("cref");
1870 XmlNode doc = e.SelectSingleNode (
1871 r.Name + "[@cref='" + cref + "']");
1872 string value = r.ReadInnerXml ().Replace ("\r", "");
1874 doc.InnerXml = value;
1876 XmlElement n = e.OwnerDocument.CreateElement (name);
1877 n.SetAttribute ("cref", cref);
1884 string name = r.Name;
1885 string xpath = r.Name;
1886 StringList attributes = new StringList (r.AttributeCount);
1887 if (r.MoveToFirstAttribute ()) {
1889 attributes.Add ("@" + r.Name + "=\"" + r.Value + "\"");
1890 } while (r.MoveToNextAttribute ());
1893 if (attributes.Count > 0) {
1894 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
1896 XmlNode doc = e.SelectSingleNode (xpath);
1897 string value = r.ReadInnerXml ().Replace ("\r", "");
1899 doc.InnerXml = value;
1902 XmlElement n = e.OwnerDocument.CreateElement (name);
1904 foreach (string a in attributes) {
1905 int eq = a.IndexOf ('=');
1906 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
1915 if (info.SlashDocs != null) {
1916 XmlNode elem = info.SlashDocs;
1918 if (elem.SelectSingleNode("summary") != null)
1919 ClearElement(e, "summary");
1920 if (elem.SelectSingleNode("remarks") != null)
1921 ClearElement(e, "remarks");
1922 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
1923 ClearElement(e, "value");
1924 ClearElement(e, "returns");
1927 foreach (XmlNode child in elem.ChildNodes) {
1928 switch (child.Name) {
1931 XmlAttribute name = child.Attributes ["name"];
1934 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
1936 p2.InnerXml = child.InnerXml;
1939 // Occasionally XML documentation will use <returns/> on
1940 // properties, so let's try to normalize things.
1943 XmlElement v = e.OwnerDocument.CreateElement (retnodename);
1944 v.InnerXml = child.InnerXml;
1950 case "permission": {
1951 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
1954 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
1956 a = e.OwnerDocument.CreateElement (child.Name);
1957 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
1960 a.InnerXml = child.InnerXml;
1964 XmlAttribute cref = child.Attributes ["cref"];
1967 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
1969 a = e.OwnerDocument.CreateElement ("altmember");
1970 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
1977 foreach (XmlElement n in e.SelectNodes (child.Name))
1978 if (n.OuterXml == child.OuterXml) {
1983 CopyNode (child, e);
1992 OrderDocsNodes (e, e.ChildNodes);
1993 NormalizeWhitespace(e);
1996 static readonly string[] DocsNodeOrder = {
1997 "typeparam", "param", "summary", "returns", "value", "remarks",
2000 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
2002 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
2003 for (int i = 0; i < DocsNodeOrder.Length; ++i) {
2004 for (int j = 0; j < children.Count; ++j) {
2005 XmlNode c = children [j];
2006 if (c.Name == DocsNodeOrder [i]) {
2007 newChildren.Add (c);
2011 if (newChildren.Count >= 0)
2012 docs.PrependChild ((XmlNode) newChildren [0]);
2013 for (int i = 1; i < newChildren.Count; ++i) {
2014 XmlNode prev = (XmlNode) newChildren [i-1];
2015 XmlNode cur = (XmlNode) newChildren [i];
2016 docs.RemoveChild (cur);
2017 docs.InsertAfter (cur, prev);
2022 private void UpdateParameters (XmlElement e, string element, string[] values)
2024 if (values != null) {
2025 XmlNode[] paramnodes = new XmlNode[values.Length];
2027 // Some documentation had param nodes with leading spaces.
2028 foreach (XmlElement paramnode in e.SelectNodes(element)){
2029 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
2032 // If a member has only one parameter, we can track changes to
2033 // the name of the parameter easily.
2034 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
2035 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
2038 bool reinsert = false;
2040 // Pick out existing and still-valid param nodes, and
2041 // create nodes for parameters not in the file.
2042 Hashtable seenParams = new Hashtable();
2043 for (int pi = 0; pi < values.Length; pi++) {
2044 string p = values [pi];
2047 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
2048 if (paramnodes[pi] != null) continue;
2050 XmlElement pe = e.OwnerDocument.CreateElement(element);
2051 pe.SetAttribute("name", p);
2052 pe.InnerText = "To be added.";
2053 paramnodes[pi] = pe;
2057 // Remove parameters that no longer exist and check all params are in the right order.
2059 MyXmlNodeList todelete = new MyXmlNodeList ();
2060 foreach (XmlElement paramnode in e.SelectNodes(element)) {
2061 string name = paramnode.GetAttribute("name");
2062 if (!seenParams.ContainsKey(name)) {
2063 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2064 Warning ("The following param node can only be deleted if the --delete option is given: ");
2065 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
2067 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
2068 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2072 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
2073 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2074 e.ParentNode.Attributes ["MemberName"].Value,
2077 Warning ("\tValue={0}", paramnode.OuterXml);
2079 todelete.Add (paramnode);
2084 if ((int)seenParams[name] != idx)
2090 foreach (XmlNode n in todelete) {
2091 n.ParentNode.RemoveChild (n);
2094 // Re-insert the parameter nodes at the top of the doc section.
2096 for (int pi = values.Length-1; pi >= 0; pi--)
2097 e.PrependChild(paramnodes[pi]);
2099 // Clear all existing param nodes
2100 foreach (XmlNode paramnode in e.SelectNodes(element)) {
2101 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2102 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
2103 Console.WriteLine(paramnode.OuterXml);
2105 paramnode.ParentNode.RemoveChild(paramnode);
2111 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
2113 string existingName = pe.GetAttribute ("name");
2114 pe.SetAttribute ("name", newName);
2115 if (existingName == newName)
2117 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
2118 if (paramref.GetAttribute ("name").Trim () == existingName)
2119 paramref.SetAttribute ("name", newName);
2122 class CrefComparer : XmlNodeComparer {
2124 public CrefComparer ()
2128 public override int Compare (XmlNode x, XmlNode y)
2130 string xType = x.Attributes ["cref"].Value;
2131 string yType = y.Attributes ["cref"].Value;
2132 string xNamespace = GetNamespace (xType);
2133 string yNamespace = GetNamespace (yType);
2135 int c = xNamespace.CompareTo (yNamespace);
2138 return xType.CompareTo (yType);
2141 static string GetNamespace (string type)
2143 int n = type.LastIndexOf ('.');
2145 return type.Substring (0, n);
2146 return string.Empty;
2150 private void UpdateExceptions (XmlNode docs, IMemberReference member)
2152 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
2153 string cref = slashdocFormatter.GetDeclaration (source.Exception);
2154 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
2157 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
2158 e.SetAttribute ("cref", cref);
2159 e.InnerXml = "To be added; from: <see cref=\"" +
2160 string.Join ("\" />, <see cref=\"",
2161 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
2164 docs.AppendChild (e);
2166 SortXmlNodes (docs, docs.SelectNodes ("exception"),
2167 new CrefComparer ());
2170 private static void NormalizeWhitespace(XmlElement e) {
2171 // Remove all text and whitespace nodes from the element so it
2172 // is outputted with nice indentation and no blank lines.
2173 ArrayList deleteNodes = new ArrayList();
2174 foreach (XmlNode n in e)
2175 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2177 foreach (XmlNode n in deleteNodes)
2178 n.ParentNode.RemoveChild(n);
2181 private static bool UpdateAssemblyVersions (XmlElement root, IMemberReference member, bool add)
2183 TypeDefinition type = member as TypeDefinition;
2185 type = member.DeclaringType as TypeDefinition;
2186 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
2189 private static string GetAssemblyVersion (AssemblyDefinition assembly)
2191 return assembly.Name.Version.ToString();
2194 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
2196 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
2198 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2199 root.AppendChild(e);
2201 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
2202 .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
2204 // matches.Count > 0 && add: ignore -- already present
2205 if (matches.Count > 0 && !add) {
2206 foreach (XmlNode c in matches)
2209 else if (matches.Count == 0 && add) {
2210 foreach (string sv in assemblyVersions) {
2211 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2216 // matches.Count == 0 && !add: ignore -- already not present
2218 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2219 SortXmlNodes (e, avs, new VersionComparer ());
2221 return avs.Count != 0;
2224 // FIXME: get TypeReferences instead of string comparison?
2225 private static string[] IgnorableAttributes = {
2226 // Security related attributes
2227 "System.Reflection.AssemblyKeyFileAttribute",
2228 "System.Reflection.AssemblyDelaySignAttribute",
2229 // Present in @RefType
2230 "System.Runtime.InteropServices.OutAttribute",
2231 // For naming the indexer to use when not using indexers
2232 "System.Reflection.DefaultMemberAttribute",
2233 // for decimal constants
2234 "System.Runtime.CompilerServices.DecimalConstantAttribute",
2235 // compiler generated code
2236 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
2237 // more compiler generated code, e.g. iterator methods
2238 "System.Diagnostics.DebuggerHiddenAttribute",
2239 "System.Runtime.CompilerServices.FixedBufferAttribute",
2240 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
2241 // extension methods
2242 "System.Runtime.CompilerServices.ExtensionAttribute",
2245 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes)
2247 if (!attributes.Any ()) {
2248 ClearElement (root, "Attributes");
2252 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2256 e = root.OwnerDocument.CreateElement("Attributes");
2258 foreach (string attribute in attributes) {
2259 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2262 WriteElementText(ae, "AttributeName", attribute);
2265 if (e.ParentNode == null)
2266 root.AppendChild(e);
2268 NormalizeWhitespace(e);
2271 private static string MakeAttributesValueString (object v, TypeReference valueType)
2275 if (valueType.FullName == "System.Type")
2276 return "typeof(" + v.ToString () + ")";
2277 if (valueType.FullName == "System.String")
2278 return "\"" + v.ToString () + "\"";
2280 return (bool)v ? "true" : "false";
2281 TypeDefinition valueDef = valueType.Resolve ();
2282 if (valueDef == null || !valueDef.IsEnum)
2283 return v.ToString ();
2284 string typename = GetDocTypeFullName (valueType);
2285 var values = GetEnumerationValues (valueDef);
2286 long c = ToInt64 (v);
2287 if (values.ContainsKey (c))
2288 return typename + "." + values [c];
2289 if (valueDef.CustomAttributes.Cast<CustomAttribute> ()
2290 .Any (ca => ca.Constructor.DeclaringType.FullName == "System.FlagsAttribute")) {
2291 return string.Join (" | ",
2292 (from i in values.Keys
2294 select typename + "." + values [i])
2297 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
2300 private static Dictionary<long, string> GetEnumerationValues (TypeDefinition type)
2302 var values = new Dictionary<long, string> ();
2304 (from f in type.Fields.Cast<FieldDefinition> ()
2305 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
2307 values [ToInt64 (f.Constant)] = f.Name;
2312 static long ToInt64 (object value)
2315 return (long) (ulong) value;
2316 return Convert.ToInt64 (value);
2319 private void MakeParameters (XmlElement root, ParameterDefinitionCollection parameters)
2321 XmlElement e = WriteElement(root, "Parameters");
2323 foreach (ParameterDefinition p in parameters) {
2324 XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
2326 pe.SetAttribute("Name", p.Name);
2327 pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
2328 if (p.ParameterType is ReferenceType) {
2329 if (p.IsOut) pe.SetAttribute("RefType", "out");
2330 else pe.SetAttribute("RefType", "ref");
2332 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
2336 private void MakeTypeParameters (XmlElement root, GenericParameterCollection typeParams)
2338 if (typeParams == null || typeParams.Count == 0) {
2339 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2341 root.RemoveChild (f);
2344 XmlElement e = WriteElement(root, "TypeParameters");
2346 foreach (GenericParameter t in typeParams) {
2347 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2349 pe.SetAttribute("Name", t.Name);
2350 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""));
2351 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2352 ConstraintCollection constraints = t.Constraints;
2353 GenericParameterAttributes attrs = t.Attributes;
2354 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2362 ce = root.OwnerDocument.CreateElement ("Constraints");
2364 pe.AppendChild (ce);
2365 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2366 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2367 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2368 AppendElementText (ce, "ParameterAttribute", "Covariant");
2369 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2370 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2371 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2372 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2373 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2374 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2375 foreach (TypeReference c in constraints) {
2376 TypeDefinition cd = c.Resolve ();
2377 AppendElementText (ce,
2378 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2379 GetDocTypeFullName (c));
2384 private void MakeParameters (XmlElement root, IMemberReference mi)
2386 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2387 MakeParameters (root, ((MethodDefinition)mi).Parameters);
2388 else if (mi is MethodDefinition) {
2389 MethodDefinition mb = (MethodDefinition) mi;
2390 ParameterDefinitionCollection parameters = mb.Parameters;
2391 MakeParameters(root, parameters);
2392 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2393 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2394 p.SetAttribute ("RefType", "this");
2397 else if (mi is PropertyDefinition) {
2398 ParameterDefinitionCollection parameters = ((PropertyDefinition)mi).Parameters;
2399 if (parameters.Count > 0)
2400 MakeParameters(root, parameters);
2404 else if (mi is FieldDefinition) return;
2405 else if (mi is EventDefinition) return;
2406 else throw new ArgumentException();
2409 private static string GetDocParameterType (TypeReference type)
2411 return GetDocTypeFullName (type).Replace ("@", "&");
2414 private void MakeReturnValue (XmlElement root, TypeReference type, CustomAttributeCollection attributes)
2416 XmlElement e = WriteElement(root, "ReturnValue");
2418 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2419 if (attributes != null)
2420 MakeAttributes(e, GetCustomAttributes (attributes, ""));
2423 private void MakeReturnValue (XmlElement root, IMemberReference mi)
2425 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2427 else if (mi is MethodDefinition)
2428 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType.ReturnType, ((MethodDefinition)mi).ReturnType.CustomAttributes);
2429 else if (mi is PropertyDefinition)
2430 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
2431 else if (mi is FieldDefinition)
2432 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
2433 else if (mi is EventDefinition)
2434 MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
2436 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2439 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2441 IMemberReference mi = info.Member;
2442 if (mi is TypeDefinition) return null;
2444 string sigs = MakeMemberSignature(mi);
2445 if (sigs == null) return null; // not publicly visible
2447 // no documentation for property/event accessors. Is there a better way of doing this?
2448 if (mi.Name.StartsWith("get_")) return null;
2449 if (mi.Name.StartsWith("set_")) return null;
2450 if (mi.Name.StartsWith("add_")) return null;
2451 if (mi.Name.StartsWith("remove_")) return null;
2452 if (mi.Name.StartsWith("raise_")) return null;
2454 XmlElement me = doc.CreateElement("Member");
2455 me.SetAttribute("MemberName", GetMemberName (mi));
2459 if (exceptions.HasValue &&
2460 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2461 UpdateExceptions (info.Node, info.Member);
2463 if (since != null) {
2464 XmlNode docs = me.SelectSingleNode("Docs");
2465 docs.AppendChild (CreateSinceNode (doc));
2471 private static string GetMemberName (IMemberReference mi)
2473 MethodDefinition mb = mi as MethodDefinition;
2475 PropertyDefinition pi = mi as PropertyDefinition;
2478 return DocUtils.GetPropertyName (pi);
2480 StringBuilder sb = new StringBuilder (mi.Name.Length);
2481 if (!DocUtils.IsExplicitlyImplemented (mb))
2482 sb.Append (mi.Name);
2484 TypeReference iface;
2485 MethodReference ifaceMethod;
2486 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2487 sb.Append (GetDocTypeFullName (iface));
2489 sb.Append (ifaceMethod.Name);
2491 if (mb.IsGenericMethod ()) {
2492 GenericParameterCollection typeParams = mb.GenericParameters;
2493 if (typeParams.Count > 0) {
2495 sb.Append (typeParams [0].Name);
2496 for (int i = 1; i < typeParams.Count; ++i)
2497 sb.Append (",").Append (typeParams [i].Name);
2501 return sb.ToString ();
2504 private static int CountChars (string s, char c)
2507 for (int i = 0; i < s.Length; ++i) {
2514 /// SIGNATURE GENERATION FUNCTIONS
2516 static string MakeTypeSignature (TypeReference type)
2518 return csharpFormatter.GetDeclaration (type);
2521 static string MakeMemberSignature (IMemberReference mi)
2523 return csharpFullFormatter.GetDeclaration (mi);
2526 static string GetMemberType (IMemberReference mi)
2528 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2529 return "Constructor";
2530 if (mi is MethodDefinition)
2532 if (mi is PropertyDefinition)
2534 if (mi is FieldDefinition)
2536 if (mi is EventDefinition)
2538 throw new ArgumentException();
2541 private static string GetDocTypeName (TypeReference type)
2543 return docTypeFormatter.GetName (type);
2546 private static string GetDocTypeFullName (TypeReference type)
2548 return DocTypeFullMemberFormatter.Default.GetName (type);
2551 class DocsNodeInfo {
2552 public DocsNodeInfo (XmlElement node)
2557 public DocsNodeInfo (XmlElement node, TypeDefinition type)
2563 public DocsNodeInfo (XmlElement node, IMemberReference member)
2566 SetMemberInfo (member);
2569 void SetType (TypeDefinition type)
2572 throw new ArgumentNullException ("type");
2573 GenericParameters = new List<GenericParameter> (type.GenericParameters.Cast<GenericParameter> ());
2574 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
2575 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
2576 for (int i = 0; i < declTypes.Count - 1; ++i) {
2577 int remove = System.Math.Min (maxGenArgs,
2578 DocUtils.GetGenericArgumentCount (declTypes [i]));
2579 maxGenArgs -= remove;
2580 while (remove-- > 0)
2581 GenericParameters.RemoveAt (0);
2583 if (DocUtils.IsDelegate (type)) {
2584 Parameters = type.GetMethod("Invoke").Parameters;
2585 ReturnType = type.GetMethod("Invoke").ReturnType.ReturnType;
2587 SetSlashDocs (type);
2590 void SetMemberInfo (IMemberReference member)
2593 throw new ArgumentNullException ("member");
2594 ReturnIsReturn = true;
2598 if (member is MethodReference ) {
2599 MethodReference mr = (MethodReference) member;
2600 Parameters = mr.Parameters;
2601 if (mr.IsGenericMethod ()) {
2602 GenericParameters = new List<GenericParameter> (mr.GenericParameters.Cast<GenericParameter> ());
2605 else if (member is PropertyDefinition) {
2606 Parameters = ((PropertyDefinition) member).Parameters;
2609 if (member is MethodDefinition) {
2610 ReturnType = ((MethodDefinition) member).ReturnType.ReturnType;
2611 } else if (member is PropertyDefinition) {
2612 ReturnType = ((PropertyDefinition) member).PropertyType;
2613 ReturnIsReturn = false;
2616 // no remarks section for enum members
2617 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
2619 SetSlashDocs (member);
2622 private void SetSlashDocs (IMemberReference member)
2624 if (slashdocs == null)
2627 string slashdocsig = slashdocFormatter.GetDeclaration (member);
2628 if (slashdocsig != null)
2629 SlashDocs = slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
2632 public TypeReference ReturnType;
2633 public List<GenericParameter> GenericParameters;
2634 public ParameterDefinitionCollection Parameters;
2635 public bool ReturnIsReturn;
2636 public XmlElement Node;
2637 public bool AddRemarks = true;
2638 public XmlNode SlashDocs;
2639 public XmlReader EcmaDocs;
2640 public IMemberReference Member;
2643 static string GetXPathForMember (DocumentationMember member)
2645 StringBuilder xpath = new StringBuilder ();
2646 xpath.Append ("//Members/Member[@MemberName=\"")
2647 .Append (member.MemberName)
2649 if (member.Parameters != null && member.Parameters.Count > 0) {
2650 xpath.Append ("/Parameters[count(Parameter) = ")
2651 .Append (member.Parameters.Count);
2652 for (int i = 0; i < member.Parameters.Count; ++i) {
2653 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2654 xpath.Append (member.Parameters [i]);
2655 xpath.Append ("\"");
2657 xpath.Append ("]/..");
2659 return xpath.ToString ();
2662 public static string GetXPathForMember (XPathNavigator member)
2664 StringBuilder xpath = new StringBuilder ();
2665 xpath.Append ("//Type[@FullName=\"")
2666 .Append (member.SelectSingleNode ("../../@FullName").Value)
2668 xpath.Append ("Members/Member[@MemberName=\"")
2669 .Append (member.SelectSingleNode ("@MemberName").Value)
2671 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2672 if (parameters.Count > 0) {
2673 xpath.Append ("/Parameters[count(Parameter) = ")
2674 .Append (parameters.Count);
2676 while (parameters.MoveNext ()) {
2678 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2679 xpath.Append (parameters.Current.Value);
2680 xpath.Append ("\"");
2682 xpath.Append ("]/..");
2684 return xpath.ToString ();
2687 public static string GetXPathForMember (IMemberReference member)
2689 StringBuilder xpath = new StringBuilder ();
2690 xpath.Append ("//Type[@FullName=\"")
2691 .Append (member.DeclaringType.FullName)
2693 xpath.Append ("Members/Member[@MemberName=\"")
2694 .Append (GetMemberName (member))
2697 ParameterDefinitionCollection parameters = null;
2698 if (member is MethodDefinition)
2699 parameters = ((MethodDefinition) member).Parameters;
2700 else if (member is PropertyDefinition) {
2701 parameters = ((PropertyDefinition) member).Parameters;
2703 if (parameters != null && parameters.Count > 0) {
2704 xpath.Append ("/Parameters[count(Parameter) = ")
2705 .Append (parameters.Count);
2706 for (int i = 0; i < parameters.Count; ++i) {
2707 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2708 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2709 xpath.Append ("\"");
2711 xpath.Append ("]/..");
2713 return xpath.ToString ();
2717 static class CecilExtensions {
2718 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type)
2720 foreach (var c in type.Constructors)
2721 yield return (IMemberReference) c;
2722 foreach (var e in type.Events)
2723 yield return (IMemberReference) e;
2724 foreach (var f in type.Fields)
2725 yield return (IMemberReference) f;
2726 foreach (var m in type.Methods)
2727 yield return (IMemberReference) m;
2728 foreach (var t in type.NestedTypes)
2729 yield return (IMemberReference) t;
2730 foreach (var p in type.Properties)
2731 yield return (IMemberReference) p;
2734 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type, string member)
2736 return GetMembers (type).Where (m => m.Name == member);
2739 public static IMemberReference GetMember (this TypeDefinition type, string member)
2741 return GetMembers (type, member).EnsureZeroOrOne ();
2744 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2746 if (source.Count () > 1)
2747 throw new InvalidOperationException ("too many matches");
2748 return source.FirstOrDefault ();
2751 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2753 return type.Methods.Cast<MethodDefinition> ()
2754 .Where (m => m.Name == method)
2755 .EnsureZeroOrOne ();
2758 public static IEnumerable<IMemberReference> GetDefaultMembers (this TypeReference type)
2760 TypeDefinition def = type as TypeDefinition;
2762 return new IMemberReference [0];
2763 CustomAttribute defMemberAttr = type.CustomAttributes.Cast<CustomAttribute> ()
2764 .Where (c => c.Constructor.DeclaringType.FullName == "System.Reflection.DefaultMemberAttribute")
2766 if (defMemberAttr == null)
2767 return new IMemberReference [0];
2768 string name = (string) defMemberAttr.ConstructorParameters [0];
2769 return def.Properties.Cast<PropertyDefinition> ()
2770 .Where (p => p.Name == name)
2771 .Select (p => (IMemberReference) p);
2774 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2776 return assembly.Modules.Cast<ModuleDefinition> ()
2777 .SelectMany (md => md.Types.Cast<TypeDefinition> ());
2780 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2782 return GetTypes (assembly)
2783 .Where (td => td.FullName == type)
2784 .EnsureZeroOrOne ();
2787 public static bool IsGenericType (this TypeReference type)
2789 return type.GenericParameters.Count > 0;
2792 public static bool IsGenericMethod (this MethodReference method)
2794 return method.GenericParameters.Count > 0;
2797 public static IMemberReference Resolve (this IMemberReference member)
2799 EventReference er = member as EventReference;
2801 return er.Resolve ();
2802 FieldReference fr = member as FieldReference;
2804 return fr.Resolve ();
2805 MethodReference mr = member as MethodReference;
2807 return mr.Resolve ();
2808 PropertyReference pr = member as PropertyReference;
2810 return pr.Resolve ();
2811 TypeReference tr = member as TypeReference;
2813 return tr.Resolve ();
2814 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2818 static class DocUtils {
2819 public static bool IsExplicitlyImplemented (MethodDefinition method)
2821 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2824 public static string GetTypeDotMember (string name)
2826 int startType, startMethod;
2827 startType = startMethod = -1;
2828 for (int i = 0; i < name.Length; ++i) {
2829 if (name [i] == '.') {
2830 startType = startMethod;
2834 return name.Substring (startType+1);
2837 public static string GetMember (string name)
2839 int i = name.LastIndexOf ('.');
2842 return name.Substring (i+1);
2845 public static void GetInfoForExplicitlyImplementedMethod (
2846 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2850 if (method.Overrides.Count != 1)
2851 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2852 iface = method.Overrides [0].DeclaringType;
2853 ifaceMethod = method.Overrides [0];
2856 public static string GetPropertyName (PropertyDefinition pi)
2858 // Issue: (g)mcs-generated assemblies that explicitly implement
2859 // properties don't specify the full namespace, just the
2860 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2861 MethodDefinition method = pi.GetMethod;
2863 method = pi.SetMethod;
2864 if (!IsExplicitlyImplemented (method))
2867 // Need to determine appropriate namespace for this member.
2868 TypeReference iface;
2869 MethodReference ifaceMethod;
2870 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2871 return string.Join (".", new string[]{
2872 DocTypeFullMemberFormatter.Default.GetName (iface),
2873 GetMember (pi.Name)});
2876 public static string GetNamespace (TypeReference type)
2878 if (type.GetOriginalType ().IsNested)
2879 type = type.GetOriginalType ();
2880 while (type != null && type.IsNested)
2881 type = type.DeclaringType;
2883 return string.Empty;
2884 return type.Namespace;
2887 public static string PathCombine (string dir, string path)
2893 return Path.Combine (dir, path);
2896 public static bool IsExtensionMethod (MethodDefinition method)
2899 method.CustomAttributes.Cast<CustomAttribute> ()
2900 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2902 method.DeclaringType.CustomAttributes.Cast<CustomAttribute> ()
2903 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2907 public static bool IsDelegate (TypeDefinition type)
2909 TypeReference baseRef = type.BaseType;
2910 if (baseRef == null)
2912 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2913 baseRef.FullName == "System.MulticastDelegate";
2916 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2918 List<TypeReference> decls = new List<TypeReference> ();
2920 while (type.DeclaringType != null) {
2921 decls.Add (type.DeclaringType);
2922 type = type.DeclaringType;
2928 public static int GetGenericArgumentCount (TypeReference type)
2930 GenericInstanceType inst = type as GenericInstanceType;
2932 ? inst.GenericArguments.Count
2933 : type.GenericParameters.Count;
2936 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
2938 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
2939 List<TypeReference> userInterfaces = new List<TypeReference> ();
2940 foreach (TypeReference iface in type.Interfaces) {
2941 TypeReference lookup = iface.Resolve () ?? iface;
2942 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
2943 userInterfaces.Add (iface);
2945 return userInterfaces;
2948 private static string GetQualifiedTypeName (TypeReference type)
2950 return "[" + type.Scope.Name + "]" + type.FullName;
2953 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
2955 HashSet<string> inheritedInterfaces = new HashSet<string> ();
2956 Action<TypeDefinition> a = null;
2958 if (t == null) return;
2959 foreach (TypeReference r in t.Interfaces) {
2960 inheritedInterfaces.Add (GetQualifiedTypeName (r));
2964 TypeReference baseRef = type.BaseType;
2965 while (baseRef != null) {
2966 TypeDefinition baseDef = baseRef.Resolve ();
2967 if (baseDef != null) {
2969 baseRef = baseDef.BaseType;
2974 foreach (TypeReference r in type.Interfaces)
2976 return inheritedInterfaces;
2980 class DocumentationMember {
2981 public StringToStringMap MemberSignatures = new StringToStringMap ();
2982 public string ReturnType;
2983 public StringList Parameters;
2984 public string MemberName;
2985 public string MemberType;
2987 public DocumentationMember (XmlReader reader)
2989 MemberName = reader.GetAttribute ("MemberName");
2990 int depth = reader.Depth;
2992 StringList p = new StringList ();
2994 if (reader.NodeType != XmlNodeType.Element)
2996 switch (reader.Name) {
2997 case "MemberSignature":
2998 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3001 MemberType = reader.ReadElementString ();
3004 if (reader.Depth == depth + 2)
3005 ReturnType = reader.ReadElementString ();
3008 if (reader.Depth == depth + 2)
3009 p.Add (reader.GetAttribute ("Type"));
3012 if (reader.Depth == depth + 1)
3016 } while (go && reader.Read () && reader.Depth >= depth);
3022 public DocumentationMember (XmlNode node)
3024 MemberName = node.Attributes ["MemberName"].Value;
3025 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3026 XmlAttribute l = n.Attributes ["Language"];
3027 XmlAttribute v = n.Attributes ["Value"];
3028 if (l != null && v != null)
3029 MemberSignatures [l.Value] = v.Value;
3031 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3032 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
3034 ReturnType = rt.InnerText;
3035 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
3037 Parameters = new StringList (p.Count);
3038 for (int i = 0; i < p.Count; ++i)
3039 Parameters.Add (p [i].Attributes ["Type"].Value);
3044 public enum MemberFormatterState {
3047 WithinGenericTypeContainer,
3050 public abstract class MemberFormatter {
3051 public virtual string GetName (IMemberReference member)
3053 TypeReference type = member as TypeReference;
3055 return GetTypeName (type);
3056 MethodReference method = member as MethodReference;
3057 if (method != null && method.Name == ".ctor") // method.IsConstructor
3058 return GetConstructorName (method);
3060 return GetMethodName (method);
3061 PropertyReference prop = member as PropertyReference;
3063 return GetPropertyName (prop);
3064 FieldReference field = member as FieldReference;
3066 return GetFieldName (field);
3067 EventReference e = member as EventReference;
3069 return GetEventName (e);
3070 throw new NotSupportedException ("Can't handle: " +
3071 (member == null ? "null" : member.GetType().ToString()));
3074 protected virtual string GetTypeName (TypeReference type)
3077 throw new ArgumentNullException ("type");
3078 return _AppendTypeName (new StringBuilder (type.Name.Length), type).ToString ();
3081 protected virtual char[] ArrayDelimeters {
3082 get {return new char[]{'[', ']'};}
3085 protected virtual MemberFormatterState MemberFormatterState { get; set; }
3087 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type)
3089 if (type is ArrayType) {
3090 TypeSpecification spec = type as TypeSpecification;
3091 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3092 .Append (ArrayDelimeters [0]);
3093 var origState = MemberFormatterState;
3094 MemberFormatterState = MemberFormatterState.WithinArray;
3095 ArrayType array = (ArrayType) type;
3096 int rank = array.Rank;
3098 buf.Append (new string (',', rank-1));
3099 MemberFormatterState = origState;
3100 return buf.Append (ArrayDelimeters [1]);
3102 if (type is ReferenceType) {
3103 return AppendRefTypeName (buf, type);
3105 if (type is PointerType) {
3106 return AppendPointerTypeName (buf, type);
3108 AppendNamespace (buf, type);
3109 if (type is GenericParameter) {
3110 return AppendTypeName (buf, type);
3112 GenericInstanceType genInst = type as GenericInstanceType;
3113 if (type.GenericParameters.Count == 0 &&
3114 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3115 return AppendFullTypeName (buf, type);
3117 return AppendGenericType (buf, type);
3120 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3122 string ns = DocUtils.GetNamespace (type);
3123 if (ns != null && ns.Length > 0)
3124 buf.Append (ns).Append ('.');
3128 private StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type)
3130 if (type.DeclaringType != null)
3131 AppendFullTypeName (buf, type.DeclaringType).Append (NestedTypeSeparator);
3132 return AppendTypeName (buf, type);
3135 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3137 return AppendTypeName (buf, type.Name);
3140 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3142 int n = typename.IndexOf ("`");
3144 return buf.Append (typename.Substring (0, n));
3145 return buf.Append (typename);
3148 protected virtual string RefTypeModifier {
3152 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type)
3154 TypeSpecification spec = type as TypeSpecification;
3155 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3156 .Append (RefTypeModifier);
3159 protected virtual string PointerModifier {
3163 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type)
3165 TypeSpecification spec = type as TypeSpecification;
3166 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3167 .Append (PointerModifier);
3170 protected virtual char[] GenericTypeContainer {
3171 get {return new char[]{'<', '>'};}
3174 protected virtual char NestedTypeSeparator {
3178 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3180 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3181 type is GenericInstanceType ? type.GetOriginalType () : type);
3182 List<TypeReference> genArgs = GetGenericArguments (type);
3185 bool insertNested = false;
3186 foreach (var decl in decls) {
3187 TypeReference declDef = decl.Resolve () ?? decl;
3189 buf.Append (NestedTypeSeparator);
3191 insertNested = true;
3192 AppendTypeName (buf, declDef);
3193 int ac = DocUtils.GetGenericArgumentCount (declDef);
3197 buf.Append (GenericTypeContainer [0]);
3198 var origState = MemberFormatterState;
3199 MemberFormatterState = MemberFormatterState.WithinGenericTypeContainer;
3200 _AppendTypeName (buf, genArgs [argIdx++]);
3201 for (int i = 1; i < c; ++i)
3202 _AppendTypeName (buf.Append (","), genArgs [argIdx++]);
3203 MemberFormatterState = origState;
3204 buf.Append (GenericTypeContainer [1]);
3210 private List<TypeReference> GetGenericArguments (TypeReference type)
3212 var args = new List<TypeReference> ();
3213 GenericInstanceType inst = type as GenericInstanceType;
3215 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
3217 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
3221 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3226 protected virtual string GetConstructorName (MethodReference constructor)
3228 return constructor.Name;
3231 protected virtual string GetMethodName (MethodReference method)
3236 protected virtual string GetPropertyName (PropertyReference property)
3238 return property.Name;
3241 protected virtual string GetFieldName (FieldReference field)
3246 protected virtual string GetEventName (EventReference e)
3251 public virtual string GetDeclaration (IMemberReference member)
3254 throw new ArgumentNullException ("member");
3255 TypeDefinition type = member as TypeDefinition;
3257 return GetTypeDeclaration (type);
3258 MethodDefinition method = member as MethodDefinition;
3259 if (method != null && method.IsConstructor)
3260 return GetConstructorDeclaration (method);
3262 return GetMethodDeclaration (method);
3263 PropertyDefinition prop = member as PropertyDefinition;
3265 return GetPropertyDeclaration (prop);
3266 FieldDefinition field = member as FieldDefinition;
3268 return GetFieldDeclaration (field);
3269 EventDefinition e = member as EventDefinition;
3271 return GetEventDeclaration (e);
3272 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3275 protected virtual string GetTypeDeclaration (TypeDefinition type)
3278 throw new ArgumentNullException ("type");
3279 StringBuilder buf = new StringBuilder (type.Name.Length);
3280 _AppendTypeName (buf, type);
3281 AppendGenericTypeConstraints (buf, type);
3282 return buf.ToString ();
3285 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
3287 return GetConstructorName (constructor);
3290 protected virtual string GetMethodDeclaration (MethodDefinition method)
3292 // Special signature for destructors.
3293 if (method.Name == "Finalize" && method.Parameters.Count == 0)
3294 return GetFinalizerName (method);
3296 StringBuilder buf = new StringBuilder ();
3298 AppendVisibility (buf, method);
3299 if (buf.Length == 0 &&
3300 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3303 AppendModifiers (buf, method);
3305 if (buf.Length != 0)
3307 buf.Append (GetName (method.ReturnType.ReturnType)).Append (" ");
3309 AppendMethodName (buf, method);
3310 AppendGenericMethod (buf, method).Append (" ");
3311 AppendParameters (buf, method, method.Parameters);
3312 AppendGenericMethodConstraints (buf, method);
3313 return buf.ToString ();
3316 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3318 return buf.Append (method.Name);
3321 protected virtual string GetFinalizerName (MethodDefinition method)
3326 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3331 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3336 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3341 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3346 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3351 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
3353 return GetPropertyName (property);
3356 protected virtual string GetFieldDeclaration (FieldDefinition field)
3358 return GetFieldName (field);
3361 protected virtual string GetEventDeclaration (EventDefinition e)
3363 return GetEventName (e);
3367 class CSharpFullMemberFormatter : MemberFormatter {
3369 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3371 string ns = DocUtils.GetNamespace (type);
3372 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
3373 buf.Append (ns).Append ('.');
3377 private string GetCSharpType (string t)
3380 case "System.Byte": return "byte";
3381 case "System.SByte": return "sbyte";
3382 case "System.Int16": return "short";
3383 case "System.Int32": return "int";
3384 case "System.Int64": return "long";
3386 case "System.UInt16": return "ushort";
3387 case "System.UInt32": return "uint";
3388 case "System.UInt64": return "ulong";
3390 case "System.Single": return "float";
3391 case "System.Double": return "double";
3392 case "System.Decimal": return "decimal";
3393 case "System.Boolean": return "bool";
3394 case "System.Char": return "char";
3395 case "System.Void": return "void";
3396 case "System.String": return "string";
3397 case "System.Object": return "object";
3402 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3404 if (type is GenericParameter)
3405 return AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
3406 string t = type.FullName;
3407 if (!t.StartsWith ("System.")) {
3408 return base.AppendTypeName (buf, type);
3411 string s = GetCSharpType (t);
3413 return buf.Append (s);
3415 return base.AppendTypeName (buf, type);
3418 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
3420 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeContainer)
3422 GenericParameterAttributes attrs = type.Attributes;
3423 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
3424 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
3428 buf.Append ("out ");
3432 protected override string GetTypeDeclaration (TypeDefinition type)
3434 string visibility = GetTypeVisibility (type.Attributes);
3435 if (visibility == null)
3438 StringBuilder buf = new StringBuilder ();
3440 buf.Append (visibility);
3443 MemberFormatter full = new CSharpFullMemberFormatter ();
3445 if (DocUtils.IsDelegate (type)) {
3446 buf.Append("delegate ");
3447 MethodDefinition invoke = type.GetMethod ("Invoke");
3448 buf.Append (full.GetName (invoke.ReturnType.ReturnType)).Append (" ");
3449 buf.Append (GetName (type));
3450 AppendParameters (buf, invoke, invoke.Parameters);
3451 AppendGenericTypeConstraints (buf, type);
3454 return buf.ToString();
3457 if (type.IsAbstract && !type.IsInterface)
3458 buf.Append("abstract ");
3459 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
3460 buf.Append("sealed ");
3461 buf.Replace ("abstract sealed", "static");
3463 buf.Append (GetTypeKind (type));
3465 buf.Append (GetCSharpType (type.FullName) == null
3470 TypeReference basetype = type.BaseType;
3471 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
3474 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
3475 .Select (iface => full.GetName (iface))
3479 if (basetype != null || interface_names.Count > 0)
3482 if (basetype != null) {
3483 buf.Append (full.GetName (basetype));
3484 if (interface_names.Count > 0)
3488 for (int i = 0; i < interface_names.Count; i++){
3491 buf.Append (interface_names [i]);
3493 AppendGenericTypeConstraints (buf, type);
3496 return buf.ToString ();
3499 static string GetTypeKind (TypeDefinition t)
3505 if (t.IsClass || t.FullName == "System.Enum")
3509 throw new ArgumentException(t.FullName);
3512 static string GetTypeVisibility (TypeAttributes ta)
3514 switch (ta & TypeAttributes.VisibilityMask) {
3515 case TypeAttributes.Public:
3516 case TypeAttributes.NestedPublic:
3519 case TypeAttributes.NestedFamily:
3520 case TypeAttributes.NestedFamORAssem:
3528 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3530 if (type.GenericParameters.Count == 0)
3532 return AppendConstraints (buf, type.GenericParameters);
3535 private StringBuilder AppendConstraints (StringBuilder buf, GenericParameterCollection genArgs)
3537 foreach (GenericParameter genArg in genArgs) {
3538 GenericParameterAttributes attrs = genArg.Attributes;
3539 ConstraintCollection constraints = genArg.Constraints;
3540 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
3543 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
3544 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
3545 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
3548 if (!isref && !isvt && !isnew && constraints.Count == 0)
3550 buf.Append (" where ").Append (genArg.Name).Append (" : ");
3552 buf.Append ("class");
3556 buf.Append ("struct");
3559 if (constraints.Count > 0 && !isvt) {
3562 buf.Append (GetTypeName (constraints [0]));
3563 for (int i = 1; i < constraints.Count; ++i)
3564 buf.Append (", ").Append (GetTypeName (constraints [i]));
3566 if (isnew && !isvt) {
3569 buf.Append ("new()");
3575 protected override string GetConstructorDeclaration (MethodDefinition constructor)
3577 StringBuilder buf = new StringBuilder ();
3578 AppendVisibility (buf, constructor);
3579 if (buf.Length == 0)
3583 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
3584 AppendParameters (buf, constructor, constructor.Parameters);
3587 return buf.ToString ();
3590 protected override string GetMethodDeclaration (MethodDefinition method)
3592 string decl = base.GetMethodDeclaration (method);
3598 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3600 if (DocUtils.IsExplicitlyImplemented (method)) {
3601 TypeReference iface;
3602 MethodReference ifaceMethod;
3603 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3604 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3606 .Append (ifaceMethod.Name);
3608 return base.AppendMethodName (buf, method);
3611 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3613 if (method.GenericParameters.Count == 0)
3615 return AppendConstraints (buf, method.GenericParameters);
3618 protected override string RefTypeModifier {
3622 protected override string GetFinalizerName (MethodDefinition method)
3624 return "~" + method.DeclaringType.Name + " ()";
3627 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3631 if (method.IsPublic)
3632 return buf.Append ("public");
3633 if (method.IsFamily || method.IsFamilyOrAssembly)
3634 return buf.Append ("protected");
3638 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3640 string modifiers = String.Empty;
3641 if (method.IsStatic) modifiers += " static";
3642 if (method.IsVirtual && !method.IsAbstract) {
3643 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3644 else modifiers += " override";
3646 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
3647 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
3648 if (method.IsFinal) modifiers += " sealed";
3649 if (modifiers == " virtual sealed") modifiers = "";
3651 return buf.Append (modifiers);
3654 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3656 if (method.IsGenericMethod ()) {
3657 GenericParameterCollection args = method.GenericParameters;
3658 if (args.Count > 0) {
3660 buf.Append (args [0].Name);
3661 for (int i = 1; i < args.Count; ++i)
3662 buf.Append (",").Append (args [i].Name);
3669 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3671 return AppendParameters (buf, method, parameters, '(', ')');
3674 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters, char begin, char end)
3678 if (parameters.Count > 0) {
3679 if (DocUtils.IsExtensionMethod (method))
3680 buf.Append ("this ");
3681 AppendParameter (buf, parameters [0]);
3682 for (int i = 1; i < parameters.Count; ++i) {
3684 AppendParameter (buf, parameters [i]);
3688 return buf.Append (end);
3691 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
3693 if (parameter.ParameterType is ReferenceType) {
3694 if (parameter.IsOut)
3695 buf.Append ("out ");
3697 buf.Append ("ref ");
3699 buf.Append (GetName (parameter.ParameterType)).Append (" ");
3700 return buf.Append (parameter.Name);
3703 protected override string GetPropertyDeclaration (PropertyDefinition property)
3705 MethodDefinition method;
3707 string get_visible = null;
3708 if ((method = property.GetMethod) != null &&
3709 (DocUtils.IsExplicitlyImplemented (method) ||
3710 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3711 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3712 string set_visible = null;
3713 if ((method = property.SetMethod) != null &&
3714 (DocUtils.IsExplicitlyImplemented (method) ||
3715 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3716 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3718 if ((set_visible == null) && (get_visible == null))
3722 StringBuilder buf = new StringBuilder ();
3723 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
3724 buf.Append (visibility = get_visible);
3725 else if (set_visible != null && get_visible == null)
3726 buf.Append (visibility = set_visible);
3728 buf.Append (visibility = "public");
3730 // Pick an accessor to use for static/virtual/override/etc. checks.
3731 method = property.SetMethod;
3733 method = property.GetMethod;
3735 string modifiers = String.Empty;
3736 if (method.IsStatic) modifiers += " static";
3737 if (method.IsVirtual && !method.IsAbstract) {
3738 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
3739 modifiers += " virtual";
3741 modifiers += " override";
3743 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
3744 if (method.IsAbstract && !declDef.IsInterface)
3745 modifiers += " abstract";
3747 modifiers += " sealed";
3748 if (modifiers == " virtual sealed")
3750 buf.Append (modifiers).Append (' ');
3752 buf.Append (GetName (property.PropertyType)).Append (' ');
3754 IEnumerable<IMemberReference> defs = property.DeclaringType.GetDefaultMembers ();
3755 string name = property.Name;
3756 foreach (IMemberReference mi in defs) {
3757 if (mi == property) {
3762 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
3764 if (property.Parameters.Count != 0) {
3765 AppendParameters (buf, method, property.Parameters, '[', ']');
3769 if (set_visible != null) {
3770 if (set_visible != visibility)
3771 buf.Append (' ').Append (set_visible);
3772 buf.Append (" set;");
3774 if (get_visible != null) {
3775 if (get_visible != visibility)
3776 buf.Append (' ').Append (get_visible);
3777 buf.Append (" get;");
3781 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
3784 protected override string GetFieldDeclaration (FieldDefinition field)
3786 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
3787 if (declType.IsEnum && field.Name == "value__")
3788 return null; // This member of enums aren't documented.
3790 StringBuilder buf = new StringBuilder ();
3791 AppendFieldVisibility (buf, field);
3792 if (buf.Length == 0)
3795 if (declType.IsEnum)
3798 if (field.IsStatic && !field.IsLiteral)
3799 buf.Append (" static");
3800 if (field.IsInitOnly)
3801 buf.Append (" readonly");
3802 if (field.IsLiteral)
3803 buf.Append (" const");
3805 buf.Append (' ').Append (GetName (field.FieldType)).Append (' ');
3806 buf.Append (field.Name);
3807 AppendFieldValue (buf, field);
3810 return buf.ToString ();
3813 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
3816 return buf.Append ("public");
3817 if (field.IsFamily || field.IsFamilyOrAssembly)
3818 return buf.Append ("protected");
3822 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
3824 // enums have a value__ field, which we ignore
3825 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
3826 field.DeclaringType.IsGenericType ())
3828 if (field.HasConstant && field.IsLiteral) {
3831 val = field.Constant;
3836 buf.Append (" = ").Append ("null");
3837 else if (val is Enum)
3838 buf.Append (" = ").Append (val.ToString ());
3839 else if (val is IFormattable) {
3840 string value = ((IFormattable)val).ToString();
3842 value = "\"" + value + "\"";
3843 buf.Append (" = ").Append (value);
3849 protected override string GetEventDeclaration (EventDefinition e)
3851 StringBuilder buf = new StringBuilder ();
3852 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
3856 AppendModifiers (buf, e.AddMethod);
3858 buf.Append (" event ");
3859 buf.Append (GetName (e.EventType)).Append (' ');
3860 buf.Append (e.Name).Append (';');
3862 return buf.ToString ();
3866 class CSharpMemberFormatter : CSharpFullMemberFormatter {
3867 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3873 class DocTypeFullMemberFormatter : MemberFormatter {
3874 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
3876 protected override char NestedTypeSeparator {
3881 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
3882 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3888 class SlashDocMemberFormatter : MemberFormatter {
3890 protected override char[] GenericTypeContainer {
3891 get {return new char[]{'{', '}'};}
3894 private bool AddTypeCount = true;
3896 private TypeReference genDeclType;
3897 private MethodReference genDeclMethod;
3899 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3901 if (type is GenericParameter) {
3903 if (genDeclType != null) {
3904 GenericParameterCollection genArgs = genDeclType.GenericParameters;
3905 for (int i = 0; i < genArgs.Count; ++i) {
3906 if (genArgs [i].Name == type.Name) {
3907 buf.Append ('`').Append (i);
3912 if (genDeclMethod != null) {
3913 GenericParameterCollection genArgs = null;
3914 if (genDeclMethod.IsGenericMethod ()) {
3915 genArgs = genDeclMethod.GenericParameters;
3916 for (int i = 0; i < genArgs.Count; ++i) {
3917 if (genArgs [i].Name == type.Name) {
3918 buf.Append ("``").Append (i);
3924 if (genDeclType == null && genDeclMethod == null) {
3925 // Probably from within an explicitly implemented interface member,
3926 // where CSC uses parameter names instead of indices (why?), e.g.
3927 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
3928 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
3929 buf.Append (type.Name);
3931 if (buf.Length == l) {
3932 throw new Exception (string.Format (
3933 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
3934 type.Name, genDeclType, genDeclMethod));
3938 base.AppendTypeName (buf, type);
3940 int numArgs = type.GenericParameters.Count;
3941 if (type.DeclaringType != null)
3942 numArgs -= type.GenericParameters.Count;
3944 buf.Append ('`').Append (numArgs);
3951 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3954 base.AppendGenericType (buf, type);
3956 AppendType (buf, type);
3960 private StringBuilder AppendType (StringBuilder buf, TypeReference type)
3962 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
3963 bool insertNested = false;
3964 int prevParamCount = 0;
3965 foreach (var decl in decls) {
3967 buf.Append (NestedTypeSeparator);
3968 insertNested = true;
3969 base.AppendTypeName (buf, decl);
3970 int argCount = DocUtils.GetGenericArgumentCount (decl);
3971 int numArgs = argCount - prevParamCount;
3972 prevParamCount = argCount;
3974 buf.Append ('`').Append (numArgs);
3979 public override string GetDeclaration (IMemberReference member)
3981 TypeReference r = member as TypeReference;
3983 return "T:" + GetTypeName (r);
3985 return base.GetDeclaration (member);
3988 protected override string GetConstructorName (MethodReference constructor)
3990 return GetMethodDefinitionName (constructor, "#ctor");
3993 protected override string GetMethodName (MethodReference method)
3996 MethodDefinition methodDef = method as MethodDefinition;
3997 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
4000 TypeReference iface;
4001 MethodReference ifaceMethod;
4002 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
4003 AddTypeCount = false;
4004 name = GetTypeName (iface) + "." + ifaceMethod.Name;
4005 AddTypeCount = true;
4007 return GetMethodDefinitionName (method, name);
4010 private string GetMethodDefinitionName (MethodReference method, string name)
4012 StringBuilder buf = new StringBuilder ();
4013 buf.Append (GetTypeName (method.DeclaringType));
4015 buf.Append (name.Replace (".", "#"));
4016 if (method.IsGenericMethod ()) {
4017 GenericParameterCollection genArgs = method.GenericParameters;
4018 if (genArgs.Count > 0)
4019 buf.Append ("``").Append (genArgs.Count);
4021 ParameterDefinitionCollection parameters = method.Parameters;
4023 genDeclType = method.DeclaringType;
4024 genDeclMethod = method;
4025 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
4029 genDeclMethod = null;
4031 return buf.ToString ();
4034 private StringBuilder AppendParameters (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinitionCollection parameters)
4036 if (parameters.Count == 0)
4041 AppendParameter (buf, genArgs, parameters [0]);
4042 for (int i = 1; i < parameters.Count; ++i) {
4044 AppendParameter (buf, genArgs, parameters [i]);
4047 return buf.Append (')');
4050 private StringBuilder AppendParameter (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinition parameter)
4052 AddTypeCount = false;
4053 buf.Append (GetTypeName (parameter.ParameterType));
4054 AddTypeCount = true;
4058 protected override string GetPropertyName (PropertyReference property)
4062 PropertyDefinition propertyDef = property as PropertyDefinition;
4063 MethodDefinition method = null;
4064 if (propertyDef != null)
4065 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
4066 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
4067 name = property.Name;
4069 TypeReference iface;
4070 MethodReference ifaceMethod;
4071 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4072 AddTypeCount = false;
4073 name = string.Join ("#", new string[]{
4074 GetTypeName (iface).Replace (".", "#"),
4075 DocUtils.GetMember (property.Name)
4077 AddTypeCount = true;
4080 StringBuilder buf = new StringBuilder ();
4081 buf.Append (GetName (property.DeclaringType));
4084 ParameterDefinitionCollection parameters = property.Parameters;
4085 if (parameters.Count > 0) {
4086 genDeclType = property.DeclaringType;
4088 GenericParameterCollection genArgs = property.DeclaringType.GenericParameters;
4089 AppendParameter (buf, genArgs, parameters [0]);
4090 for (int i = 1; i < parameters.Count; ++i) {
4092 AppendParameter (buf, genArgs, parameters [i]);
4097 return buf.ToString ();
4100 protected override string GetFieldName (FieldReference field)
4102 return string.Format ("{0}.{1}",
4103 GetName (field.DeclaringType), field.Name);
4106 protected override string GetEventName (EventReference e)
4108 return string.Format ("{0}.{1}",
4109 GetName (e.DeclaringType), e.Name);
4112 protected override string GetTypeDeclaration (TypeDefinition type)
4114 string name = GetName (type);
4120 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4122 string name = GetName (constructor);
4128 protected override string GetMethodDeclaration (MethodDefinition method)
4130 string name = GetName (method);
4133 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4134 genDeclType = method.DeclaringType;
4135 genDeclMethod = method;
4136 name += "~" + GetName (method.ReturnType.ReturnType);
4138 genDeclMethod = null;
4143 protected override string GetPropertyDeclaration (PropertyDefinition property)
4145 string name = GetName (property);
4151 protected override string GetFieldDeclaration (FieldDefinition field)
4153 string name = GetName (field);
4159 protected override string GetEventDeclaration (EventDefinition e)
4161 string name = GetName (e);
4168 class FileNameMemberFormatter : SlashDocMemberFormatter {
4169 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4174 protected override char NestedTypeSeparator {