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.Globalization;
13 using System.Xml.XPath;
18 using MyXmlNodeList = System.Collections.Generic.List<System.Xml.XmlNode>;
19 using StringList = System.Collections.Generic.List<string>;
20 using StringToStringMap = System.Collections.Generic.Dictionary<string, string>;
21 using StringToXmlNodeMap = System.Collections.Generic.Dictionary<string, System.Xml.XmlNode>;
23 namespace Mono.Documentation {
25 class MDocUpdaterOptions
27 public string path = null;
28 public string updateto = null;
29 public List<string> assembly = null;
30 public List<string> type = null;
31 public string @namespace = null;
32 public bool delete = false;
33 public bool overrides = true;
34 public bool ignoremembers = false;
35 public bool ignore_extra_docs = false;
36 public bool no_assembly_versions = false;
39 public bool pretty = true;
41 public bool show_exceptions;
42 public ExceptionLocations? exceptions;
45 class MDocUpdater : MDocCommand
48 static string srcPath;
49 static List<AssemblyDefinition> assemblies;
51 static bool nooverrides = true, delete = false, ignoremembers = false;
52 static bool pretty = false;
53 static bool show_exceptions = false;
54 static bool no_assembly_versions;
55 static ExceptionLocations? exceptions;
57 static int additions = 0, deletions = 0;
60 static XmlDocument slashdocs;
61 static XmlReader ecmadocs;
65 static MemberFormatter csharpFullFormatter = new CSharpFullMemberFormatter ();
66 static MemberFormatter csharpFormatter = new CSharpMemberFormatter ();
67 static MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
68 static MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
69 static MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
71 static MyXmlNodeList extensionMethods = new MyXmlNodeList ();
73 public override void Run (IEnumerable<string> args)
75 var opts = new MDocUpdaterOptions {
78 show_exceptions = DebugOutput,
81 opts.type = new List<string> ();
82 var p = new OptionSet () {
84 "Delete removed members from the XML files.",
85 v => opts.delete = v != null },
87 "Document potential exceptions that members can generate. {SOURCES} " +
88 "is a comma-separated list of:\n" +
89 " asm Method calls in same assembly\n" +
90 " depasm Method calls in dependent assemblies\n" +
91 " all Record all possible exceptions\n" +
92 "If nothing is specified, then only exceptions from the member will " +
94 v => opts.exceptions = ParseExceptionLocations (v) },
96 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
99 case "no-assembly-versions":
100 opts.no_assembly_versions = true;
103 throw new Exception ("Unsupported flag `" + v + "'.");
106 { "fno-assembly-versions",
107 "Do not generate //AssemblyVersion elements.",
108 v => opts.no_assembly_versions = v != null },
110 "Import documentation from {FILE}.",
111 v => opts.import = v },
113 "Root {DIRECTORY} to generate/update documentation.",
114 v => opts.path = v },
116 "Manually specify the assembly {VERSION} that new members were added in.",
117 v => opts.since = v },
119 "Only update documentation for {TYPE}.",
120 v => opts.type.Add (v) },
122 opts.assembly = Parse (p, args, "update",
123 "[OPTIONS]+ ASSEMBLIES",
124 "Create or update documentation from ASSEMBLIES.");
125 if (opts.assembly == null)
127 if (opts.assembly.Count == 0)
128 base.Error ("No assemblies specified.");
131 opts.name = ""; // remove warning about unused member
134 static ExceptionLocations ParseExceptionLocations (string s)
136 ExceptionLocations loc = ExceptionLocations.Member;
139 foreach (var type in s.Split (',')) {
141 case "asm": loc |= ExceptionLocations.Assembly; break;
142 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
143 case "all": loc = ExceptionLocations.All; break;
144 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
150 static void Run (MDocUpdaterOptions opts)
152 nooverrides = !opts.overrides;
153 delete = opts.delete;
154 ignoremembers = opts.ignoremembers;
156 pretty = opts.pretty;
158 show_exceptions = opts.show_exceptions;
159 exceptions = opts.exceptions;
160 no_assembly_versions = opts.no_assembly_versions;
162 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
164 if (opts.path == null)
165 throw new InvalidOperationException("The --out option is required.");
169 if (opts.type != null && opts.type.Count > 0 && opts.@namespace != null)
170 throw new InvalidOperationException("You cannot specify both 'type' and 'namespace'.");
172 if (opts.assembly == null)
173 throw new InvalidOperationException("The assembly option is required.");
175 assemblies = opts.assembly.Select (a => LoadAssembly (a)).ToList ();
177 if (opts.import != null && ecmadocs == null && slashdocs == null) {
179 XmlReader r = new XmlTextReader (opts.import);
181 while (r.NodeType != XmlNodeType.Element) {
183 throw new Exception ("Unable to read XML file: " +
186 if (r.LocalName == "doc") {
187 slashdocs = new XmlDocument();
188 slashdocs.Load (opts.import);
190 else if (r.LocalName == "Libraries") {
191 ecmadocs = new XmlTextReader (opts.import);
194 throw new Exception ("Unsupported XML format within " + opts.import);
197 } catch (Exception e) {
198 Error ("Could not load XML file: {0}", e.Message);
199 Environment.ExitCode = 1;
204 // PERFORM THE UPDATES
206 string dest_dir = opts.updateto != null ? opts.updateto : opts.path;
207 if (opts.type != null && opts.type.Count > 0)
208 DoUpdateTypes(opts.path, opts.type, dest_dir);
209 else if (opts.@namespace != null)
210 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
211 Path.Combine (dest_dir, opts.@namespace));
213 DoUpdateAssemblies(opts.path, dest_dir);
215 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
218 private static new void Error (string format, params object[] args)
220 Console.Error.Write ("mdoc: ");
221 Console.Error.WriteLine (format, args);
224 private static AssemblyDefinition LoadAssembly (string name)
226 AssemblyDefinition assembly = null;
228 assembly = AssemblyFactory.GetAssembly (name);
229 } catch (System.IO.FileNotFoundException) { }
231 if (assembly == null)
232 throw new InvalidOperationException("Assembly " + name + " not found.");
234 var r = assembly.Resolver as BaseAssemblyResolver;
235 if (r != null && name.Contains (Path.DirectorySeparatorChar)) {
236 r.AddSearchDirectory (Path.GetDirectoryName (name));
241 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
242 OrderTypeAttributes (element);
243 XmlTextWriter writer = new XmlTextWriter(output);
244 writer.Formatting = Formatting.Indented;
245 writer.Indentation = 2;
246 writer.IndentChar = ' ';
247 element.WriteTo(writer);
251 private static void OrderTypeAttributes (XmlElement e)
253 foreach (XmlElement type in e.SelectNodes ("//Type")) {
254 OrderTypeAttributes (type.Attributes);
258 static readonly string[] TypeAttributeOrder = {
259 "Name", "FullName", "FullNameSP", "Maintainer"
262 private static void OrderTypeAttributes (XmlAttributeCollection c)
264 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
265 for (int i = 0; i < c.Count; ++i) {
266 XmlAttribute a = c [i];
267 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
268 if (a.Name == TypeAttributeOrder [j]) {
274 for (int i = attrs.Length-1; i >= 0; --i) {
275 XmlAttribute n = attrs [i];
278 XmlAttribute r = null;
279 for (int j = i+1; j < attrs.Length; ++j) {
280 if (attrs [j] != null) {
288 c.InsertBefore (n, r);
292 private static XmlDocument CreateIndexStub() {
293 XmlDocument index = new XmlDocument();
295 XmlElement index_root = index.CreateElement("Overview");
296 index.AppendChild(index_root);
298 if (assemblies.Count == 0)
299 throw new Exception ("No assembly");
301 XmlElement index_assemblies = index.CreateElement("Assemblies");
302 index_root.AppendChild(index_assemblies);
304 XmlElement index_remarks = index.CreateElement("Remarks");
305 index_remarks.InnerText = "To be added.";
306 index_root.AppendChild(index_remarks);
308 XmlElement index_copyright = index.CreateElement("Copyright");
309 index_copyright.InnerText = "To be added.";
310 index_root.AppendChild(index_copyright);
312 XmlElement index_types = index.CreateElement("Types");
313 index_root.AppendChild(index_types);
318 private static void WriteNamespaceStub(string ns, string outdir) {
319 XmlDocument index = new XmlDocument();
321 XmlElement index_root = index.CreateElement("Namespace");
322 index.AppendChild(index_root);
324 index_root.SetAttribute("Name", ns);
326 XmlElement index_docs = index.CreateElement("Docs");
327 index_root.AppendChild(index_docs);
329 XmlElement index_summary = index.CreateElement("summary");
330 index_summary.InnerText = "To be added.";
331 index_docs.AppendChild(index_summary);
333 XmlElement index_remarks = index.CreateElement("remarks");
334 index_remarks.InnerText = "To be added.";
335 index_docs.AppendChild(index_remarks);
337 using (TextWriter writer = OpenWrite (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew)) {
338 WriteXml(index.DocumentElement, writer);
342 public static void DoUpdateTypes (string basepath, List<string> typenames, string dest)
344 var found = new HashSet<string> ();
345 foreach (AssemblyDefinition assembly in assemblies) {
346 foreach (DocsTypeInfo docsTypeInfo in GetTypes (assembly, typenames)) {
347 string relpath = DoUpdateType (docsTypeInfo.Type, basepath, dest, docsTypeInfo.EcmaDocs);
349 found.Add (docsTypeInfo.Type.FullName);
352 var notFound = from n in typenames where !found.Contains (n) select n;
354 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
357 public static string DoUpdateType(TypeDefinition type, string basepath, string dest, XmlReader ecmaDocsType)
359 if (type.Namespace == null)
360 Error ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
362 if (!IsPublic (type))
365 // Must get the A+B form of the type name.
366 string typename = GetTypeFileName(type);
368 string reltypefile = DocUtils.PathCombine (DocUtils.GetNamespace (type), typename + ".xml");
369 string typefile = Path.Combine (basepath, reltypefile);
370 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
372 string output = null;
375 } else if (dest == "-") {
378 output = Path.Combine (dest, reltypefile);
383 XmlDocument basefile = new XmlDocument();
384 if (!pretty) basefile.PreserveWhitespace = true;
386 basefile.Load(typefile);
387 } catch (Exception e) {
388 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
391 DoUpdateType2("Updating", basefile, type, output, false, ecmaDocsType);
394 XmlElement td = StubType(type, output, ecmaDocsType);
398 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
401 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
407 public static void DoUpdateNS(string ns, string nspath, string outpath) {
408 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
409 AssemblyDefinition assembly = assemblies [0];
411 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
412 XmlDocument basefile = new XmlDocument();
413 if (!pretty) basefile.PreserveWhitespace = true;
414 string typefile = Path.Combine(nspath, file.Name);
416 basefile.Load(typefile);
417 } catch (Exception e) {
418 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
422 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
423 TypeDefinition type = assembly.GetType(typename);
425 Error ("Type no longer in assembly: " + typename);
429 seenTypes[type] = seenTypes;
430 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false, null);
433 // Stub types not in the directory
434 foreach (DocsTypeInfo docsTypeInfo in GetTypes (assembly, null)) {
435 TypeDefinition type = docsTypeInfo.Type;
436 if (type.Namespace != ns || seenTypes.ContainsKey(type))
439 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"), docsTypeInfo.EcmaDocs);
440 if (td == null) continue;
444 private static string GetTypeFileName (TypeReference type)
446 return filenameFormatter.GetName (type);
449 public static string GetTypeFileName (string typename)
451 StringBuilder filename = new StringBuilder (typename.Length);
455 for (int i = 0; i < typename.Length; ++i) {
456 char c = typename [i];
465 filename.Append ('`').Append ((numArgs+1).ToString());
480 return filename.ToString ();
483 private static void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
485 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
486 index_assembly.SetAttribute ("Name", assembly.Name.Name);
487 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
488 MakeAttributes (index_assembly, assembly.CustomAttributes, true);
489 parent.AppendChild(index_assembly);
492 private static void DoUpdateAssemblies (string source, string dest)
494 string indexfile = dest + "/index.xml";
496 if (System.IO.File.Exists(indexfile)) {
497 index = new XmlDocument();
498 index.Load(indexfile);
501 ClearElement(index.DocumentElement, "Assembly");
502 ClearElement(index.DocumentElement, "Attributes");
504 index = CreateIndexStub();
508 string defaultTitle = "Untitled";
509 if (assemblies.Count == 1)
510 defaultTitle = assemblies[0].Name.Name;
511 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
513 WriteElementText(index.DocumentElement, "Title", name);
516 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
517 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
518 index_assemblies.RemoveAll ();
521 HashSet<string> goodfiles = new HashSet<string> ();
523 foreach (AssemblyDefinition assm in assemblies) {
524 AddIndexAssembly (assm, index_assemblies);
525 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
528 SortIndexEntries (index_types);
530 CleanupFiles (dest, goodfiles);
531 CleanupIndexTypes (index_types, goodfiles);
532 CleanupExtensions (index_types);
534 using (TextWriter writer = OpenWrite (indexfile, FileMode.Create))
535 WriteXml(index.DocumentElement, writer);
538 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
540 private static void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
542 foreach (DocsTypeInfo docTypeInfo in GetTypes (assembly, null)) {
543 TypeDefinition type = docTypeInfo.Type;
544 string typename = GetTypeFileName(type);
545 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0)
548 string reltypepath = DoUpdateType (type, source, dest, docTypeInfo.EcmaDocs);
549 if (reltypepath == null)
552 // Add namespace and type nodes into the index file as needed
553 string ns = DocUtils.GetNamespace (type);
554 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode("Namespace[@Name='" + ns + "']");
555 if (nsnode == null) {
556 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
557 nsnode.SetAttribute ("Name", ns);
558 index_types.AppendChild(nsnode);
560 string doc_typename = GetDocTypeName (type);
561 XmlElement typenode = (XmlElement)nsnode.SelectSingleNode("Type[@Name='" + typename + "']");
562 if (typenode == null) {
563 typenode = index_types.OwnerDocument.CreateElement("Type");
564 typenode.SetAttribute("Name", typename);
565 nsnode.AppendChild(typenode);
567 if (typename != doc_typename)
568 typenode.SetAttribute("DisplayName", doc_typename);
570 typenode.RemoveAttribute("DisplayName");
571 typenode.SetAttribute ("Kind", GetTypeKind (type));
573 // Ensure the namespace index file exists
574 string onsdoc = DocUtils.PathCombine (dest, type.Namespace + ".xml");
575 string nsdoc = DocUtils.PathCombine (dest, "ns-" + type.Namespace + ".xml");
576 if (File.Exists (onsdoc)) {
577 File.Move (onsdoc, nsdoc);
580 if (!File.Exists (nsdoc)) {
581 Console.WriteLine("New Namespace File: " + type.Namespace);
582 WriteNamespaceStub(type.Namespace, dest);
585 goodfiles.Add (reltypepath);
590 public TypeDefinition Type;
591 public XmlReader EcmaDocs;
593 public DocsTypeInfo (TypeDefinition type, XmlReader docs)
596 this.EcmaDocs = docs;
600 static IEnumerable<Mono.Documentation.MDocUpdater.DocsTypeInfo> GetTypes (AssemblyDefinition assembly, List<string> forTypes)
602 HashSet<string> seen = null;
603 if (forTypes != null)
605 if (ecmadocs != null) {
606 seen = new HashSet<string> ();
608 while (ecmadocs.Read ()) {
609 switch (ecmadocs.Name) {
612 typeDepth = ecmadocs.Depth;
613 if (ecmadocs.NodeType != XmlNodeType.Element)
615 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
617 string typename = ecmadocs.GetAttribute ("FullName");
618 string typename2 = GetTypeFileName (typename);
619 if (forTypes != null &&
620 forTypes.BinarySearch (typename) < 0 &&
621 typename != typename2 &&
622 forTypes.BinarySearch (typename2) < 0)
625 if ((t = assembly.GetType (typename)) == null &&
626 (t = assembly.GetType (typename2)) == null)
629 if (typename != typename2)
630 seen.Add (typename2);
631 Console.WriteLine (" Import: {0}", t.FullName);
632 yield return new DocsTypeInfo (t, ecmadocs);
640 foreach (TypeDefinition type in assembly.GetTypes()) {
641 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
643 if (seen != null && seen.Contains (type.FullName))
645 yield return new DocsTypeInfo (type, null);
646 foreach (TypeDefinition nested in type.NestedTypes)
647 yield return new DocsTypeInfo (nested, null);
651 private static void SortIndexEntries (XmlElement indexTypes)
653 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
654 XmlNodeComparer c = new AttributeNameComparer ();
655 SortXmlNodes (indexTypes, namespaces, c);
657 for (int i = 0; i < namespaces.Count; ++i)
658 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
661 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
663 MyXmlNodeList l = new MyXmlNodeList (children.Count);
664 for (int i = 0; i < children.Count; ++i)
665 l.Add (children [i]);
667 for (int i = l.Count - 1; i > 0; --i) {
668 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
672 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
674 public abstract int Compare (XmlNode x, XmlNode y);
676 public int Compare (object x, object y)
678 return Compare ((XmlNode) x, (XmlNode) y);
682 class AttributeNameComparer : XmlNodeComparer {
685 public AttributeNameComparer ()
690 public AttributeNameComparer (string attribute)
692 this.attribute = attribute;
695 public override int Compare (XmlNode x, XmlNode y)
697 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
701 class VersionComparer : XmlNodeComparer {
702 public override int Compare (XmlNode x, XmlNode y)
704 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
705 string a = GetVersion (x.InnerText);
706 string b = GetVersion (y.InnerText);
707 return new Version (a).CompareTo (new Version (b));
710 static string GetVersion (string v)
712 int n = v.IndexOf ("x");
715 return v.Substring (0, n-1);
719 private static string GetTypeKind (TypeDefinition type)
722 return "Enumeration";
723 if (type.IsValueType)
725 if (type.IsInterface)
727 if (DocUtils.IsDelegate (type))
729 if (type.IsClass || type.FullName == "System.Enum") // FIXME
731 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
734 private static bool IsPublic (TypeDefinition type)
736 TypeDefinition decl = type;
737 while (decl != null) {
738 if (!(decl.IsPublic || decl.IsNestedPublic)) {
741 decl = (TypeDefinition) decl.DeclaringType;
746 private static void CleanupFiles (string dest, HashSet<string> goodfiles)
748 // Look for files that no longer correspond to types
749 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
750 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
751 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
752 if (!goodfiles.Contains (relTypeFile)) {
753 XmlDocument doc = new XmlDocument ();
754 doc.Load (typefile.FullName);
755 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
756 if (UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
757 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
758 WriteXml(doc.DocumentElement, writer);
759 goodfiles.Add (relTypeFile);
762 string newname = typefile.FullName + ".remove";
763 try { System.IO.File.Delete(newname); } catch (Exception) { }
764 try { typefile.MoveTo(newname); } catch (Exception) { }
765 Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
771 private static TextWriter OpenWrite (string path, FileMode mode)
773 return new StreamWriter (
774 new FileStream (path, mode),
775 new UTF8Encoding (false)
779 private static string[] GetAssemblyVersions ()
781 return (from a in assemblies select GetAssemblyVersion (a)).ToArray ();
784 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
786 // Look for type nodes that no longer correspond to types
787 MyXmlNodeList remove = new MyXmlNodeList ();
788 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
789 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
790 if (!goodfiles.Contains (fulltypename)) {
791 remove.Add (typenode);
794 foreach (XmlNode n in remove)
795 n.ParentNode.RemoveChild (n);
798 private static void CleanupExtensions (XmlElement index_types)
800 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
801 if (extensionMethods.Count == 0) {
804 index_types.RemoveChild (e);
808 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
809 index_types.SelectSingleNode ("/Overview").AppendChild (e);
813 extensionMethods.Sort (DefaultExtensionMethodComparer);
814 foreach (XmlNode m in extensionMethods) {
815 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
819 class ExtensionMethodComparer : XmlNodeComparer {
820 public override int Compare (XmlNode x, XmlNode y)
822 XmlNode xLink = x.SelectSingleNode ("Member/Link");
823 XmlNode yLink = y.SelectSingleNode ("Member/Link");
825 int n = xLink.Attributes ["Type"].Value.CompareTo (
826 yLink.Attributes ["Type"].Value);
829 n = xLink.Attributes ["Member"].Value.CompareTo (
830 yLink.Attributes ["Member"].Value);
831 if (n == 0 && !object.ReferenceEquals (x, y))
832 throw new InvalidOperationException ("Duplicate extension method found!");
837 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
839 public static void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince, XmlReader ecmaDocsType)
841 Console.WriteLine(message + ": " + type.FullName);
843 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
845 // Update type metadata
846 UpdateType(basefile.DocumentElement, type, ecmaDocsType);
848 if (ecmaDocsType != null) {
849 while (ecmaDocsType.Name != "Members" && ecmaDocsType.Read ()) {
852 if (ecmaDocsType.IsEmptyElement)
856 // Update existing members. Delete member nodes that no longer should be there,
857 // and remember what members are already documented so we don't add them again.
858 if (!ignoremembers) {
859 MyXmlNodeList todelete = new MyXmlNodeList ();
860 foreach (DocsNodeInfo info in GetDocumentationMembers (basefile, type, ecmaDocsType)) {
861 XmlElement oldmember = info.Node;
862 IMemberReference oldmember2 = info.Member;
863 string sig = oldmember2 != null ? MakeMemberSignature(oldmember2) : null;
865 // Interface implementations and overrides are deleted from the docs
866 // unless the overrides option is given.
867 if (oldmember2 != null && (!IsNew(oldmember2) || sig == null))
870 // Deleted (or signature changed)
871 if (oldmember2 == null) {
872 if (UpdateAssemblyVersions (oldmember, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
874 DeleteMember ("Member Removed", output, oldmember, todelete);
879 if (seenmembers.ContainsKey (sig)) {
880 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
881 // ignore, already seen
883 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
884 DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
886 Error ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
890 // Update signature information
893 seenmembers.Add (sig, oldmember);
895 foreach (XmlElement oldmember in todelete)
896 oldmember.ParentNode.RemoveChild (oldmember);
899 if (!DocUtils.IsDelegate (type) && !ignoremembers) {
900 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
901 foreach (IMemberReference m in type.GetMembers()) {
902 if (m is TypeDefinition) continue;
904 string sig = MakeMemberSignature(m);
905 if (sig == null) continue;
906 if (seenmembers.ContainsKey(sig)) continue;
908 // To be nice on diffs, members/properties/events that are overrides or are interface implementations
910 if (!IsNew(m)) continue;
912 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
913 if (mm == null) continue;
914 members.AppendChild( mm );
916 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
921 // Import code snippets from files
922 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
923 if (!(code is XmlElement)) continue;
924 string file = ((XmlElement)code).GetAttribute("src");
925 string lang = ((XmlElement)code).GetAttribute("lang");
927 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
929 code.InnerText = src;
933 if (insertSince && since != null) {
934 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
935 docs.AppendChild (CreateSinceNode (basefile));
939 XmlElement d = basefile.DocumentElement ["Docs"];
940 XmlElement m = basefile.DocumentElement ["Members"];
941 if (d != null && m != null)
942 basefile.DocumentElement.InsertBefore (
943 basefile.DocumentElement.RemoveChild (d), m);
947 System.IO.TextWriter writer;
949 writer = Console.Out;
951 FileInfo file = new FileInfo (output);
952 if (!file.Directory.Exists) {
953 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
954 file.Directory.Create ();
956 writer = OpenWrite (output, FileMode.Create);
960 WriteXml(basefile.DocumentElement, writer);
963 private static string GetCodeSource (string lang, string file)
966 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
967 // Grab the specified region
968 string region = "#region " + file.Substring (anchorStart + 4);
969 file = file.Substring (0, anchorStart + 3);
971 using (StreamReader reader = new StreamReader (file)) {
973 StringBuilder src = new StringBuilder ();
975 while ((line = reader.ReadLine ()) != null) {
976 if (line.Trim() == region) {
977 indent = line.IndexOf (region);
980 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
985 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
988 return src.ToString ();
990 } catch (Exception e) {
991 Error ("Could not load <code/> file '{0}' region '{1}': {2}",
992 file, region, show_exceptions ? e.ToString () : e.Message);
997 using (StreamReader reader = new StreamReader (file))
998 return reader.ReadToEnd ();
999 } catch (Exception e) {
1000 Error ("Could not load <code/> file '" + file + "': " + e.Message);
1005 private static IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type, XmlReader ecmaDocsMembers)
1007 if (ecmaDocsMembers != null) {
1008 int membersDepth = ecmaDocsMembers.Depth;
1010 while (go && ecmaDocsMembers.Read ()) {
1011 switch (ecmaDocsMembers.Name) {
1013 if (membersDepth != ecmaDocsMembers.Depth - 1 || ecmaDocsMembers.NodeType != XmlNodeType.Element)
1015 DocumentationMember dm = new DocumentationMember (ecmaDocsMembers);
1016 string xp = GetXPathForMember (dm);
1017 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
1019 if (oldmember == null) {
1020 m = GetMember (type, dm);
1022 Error ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
1023 type.FullName, dm.MemberSignatures ["C#"]);
1024 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
1027 // oldmember lookup may have failed due to type parameter renames.
1029 oldmember = (XmlElement) basefile.SelectSingleNode (GetXPathForMember (m));
1030 if (oldmember == null) {
1031 XmlElement members = WriteElement(basefile.DocumentElement, "Members");
1032 oldmember = basefile.CreateElement ("Member");
1033 oldmember.SetAttribute ("MemberName", dm.MemberName);
1034 members.AppendChild (oldmember);
1035 foreach (string key in Sort (dm.MemberSignatures.Keys)) {
1036 XmlElement ms = basefile.CreateElement ("MemberSignature");
1037 ms.SetAttribute ("Language", key);
1038 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
1039 oldmember.AppendChild (ms);
1041 oldmember.SetAttribute ("__monodocer-seen__", "true");
1042 Console.WriteLine ("Member Added: {0}", MakeMemberSignature (m));
1047 m = GetMember (type, new DocumentationMember (oldmember));
1049 Error ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
1050 type.FullName, dm.MemberSignatures ["C#"]);
1053 oldmember.SetAttribute ("__monodocer-seen__", "true");
1055 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
1056 if (ecmaDocsMembers.Name != "Docs")
1057 throw new InvalidOperationException ("Found " + ecmaDocsMembers.Name + "; expected <Docs/>!");
1058 node.EcmaDocs = ecmaDocsMembers;
1063 if (membersDepth == ecmaDocsMembers.Depth && ecmaDocsMembers.NodeType == XmlNodeType.EndElement) {
1070 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
1071 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
1072 oldmember.RemoveAttribute ("__monodocer-seen__");
1075 IMemberReference m = GetMember (type, new DocumentationMember (oldmember));
1077 yield return new DocsNodeInfo (oldmember);
1080 yield return new DocsNodeInfo (oldmember, m);
1085 static void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
1087 string format = output != null
1088 ? "{0}: File='{1}'; Signature='{4}'"
1089 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1093 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1094 member.Attributes ["MemberName"].Value,
1095 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
1096 if (!delete && MemberDocsHaveUserContent (member)) {
1097 Error ("Member deletions must be enabled with the --delete option.");
1099 todelete.Add (member);
1104 class MemberComparer : XmlNodeComparer {
1105 public override int Compare (XmlNode x, XmlNode y)
1108 string xMemberName = x.Attributes ["MemberName"].Value;
1109 string yMemberName = y.Attributes ["MemberName"].Value;
1111 // generic methods *end* with '>'
1112 // it's possible for explicitly implemented generic interfaces to
1113 // contain <...> without being a generic method
1114 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1115 (r = xMemberName.CompareTo (yMemberName)) != 0)
1119 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1120 xMemberName = xMemberName.Substring (0, lt);
1121 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1122 yMemberName = yMemberName.Substring (0, lt);
1123 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1126 // if @MemberName matches, then it's either two different types of
1127 // members sharing the same name, e.g. field & property, or it's an
1128 // overloaded method.
1129 // for different type, sort based on MemberType value.
1130 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1131 y.SelectSingleNode ("MemberType").InnerText);
1135 // same type -- must be an overloaded method. Sort based on type
1136 // parameter count, then parameter count, then by the parameter
1138 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1139 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1140 if (xTypeParams.Count != yTypeParams.Count)
1141 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1142 for (int i = 0; i < xTypeParams.Count; ++i) {
1143 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1144 yTypeParams [i].Attributes ["Name"].Value);
1149 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1150 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1151 if (xParams.Count != yParams.Count)
1152 return xParams.Count <= yParams.Count ? -1 : 1;
1153 for (int i = 0; i < xParams.Count; ++i) {
1154 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1155 yParams [i].Attributes ["Type"].Value);
1159 // all parameters match, but return value might not match if it was
1160 // changed between one version and another.
1161 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1162 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1163 if (xReturn != null && yReturn != null) {
1164 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1173 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1175 private static void SortTypeMembers (XmlNode members)
1177 if (members == null)
1179 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1182 private static bool MemberDocsHaveUserContent (XmlNode e)
1184 e = (XmlElement)e.SelectSingleNode("Docs");
1185 if (e == null) return false;
1186 foreach (XmlElement d in e.SelectNodes("*"))
1187 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1192 private static bool IsNew (IMemberReference m)
1194 if (!nooverrides) return true;
1195 if (m is MethodDefinition && !IsNew ((MethodDefinition)m)) return false;
1196 if (m is PropertyDefinition && !IsNew (((PropertyDefinition)m).GetMethod)) return false;
1197 if (m is PropertyDefinition && !IsNew (((PropertyDefinition)m).SetMethod)) return false;
1198 if (m is EventDefinition && !IsNew (((EventDefinition)m).AddMethod)) return false;
1199 if (m is EventDefinition && !IsNew (((EventDefinition)m).InvokeMethod)) return false;
1200 if (m is EventDefinition && !IsNew (((EventDefinition)m).RemoveMethod)) return false;
1204 private static bool IsNew (MethodDefinition m)
1211 // UPDATE HELPER FUNCTIONS
1213 private static IMemberReference GetMember (TypeDefinition type, DocumentationMember member)
1215 string membertype = member.MemberType;
1217 string returntype = member.ReturnType;
1219 string docName = member.MemberName;
1220 string[] docTypeParams = GetTypeParameters (docName);
1222 // Loop through all members in this type with the same name
1223 foreach (IMemberReference mi in GetReflectionMembers (type, docName)) {
1224 if (mi is TypeDefinition) continue;
1225 if (GetMemberType(mi) != membertype) continue;
1227 string sig = MakeMemberSignature(mi);
1228 if (sig == null) continue; // not publicly visible
1230 ParameterDefinitionCollection pis = null;
1231 string[] typeParams = null;
1232 if (mi is MethodDefinition) {
1233 MethodDefinition mb = (MethodDefinition) mi;
1234 pis = mb.Parameters;
1235 if (docTypeParams != null && mb.IsGenericMethod ()) {
1236 GenericParameterCollection args = mb.GenericParameters;
1237 if (args.Count == docTypeParams.Length) {
1238 typeParams = args.Cast<GenericParameter> ().Select (p => p.Name).ToArray ();
1242 else if (mi is PropertyDefinition)
1243 pis = ((PropertyDefinition)mi).Parameters;
1245 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
1246 int pcount = pis == null ? 0 : pis.Count;
1247 if (mcount != pcount)
1250 MethodDefinition mDef = mi as MethodDefinition;
1251 if (mDef != null && !mDef.IsConstructor) {
1252 // Casting operators can overload based on return type.
1253 if (returntype != GetReplacedString (
1254 GetDocTypeFullName (((MethodDefinition)mi).ReturnType.ReturnType),
1255 typeParams, docTypeParams)) {
1263 for (int i = 0; i < pis.Count; i++) {
1264 string paramType = GetReplacedString (
1265 GetDocParameterType (pis [i].ParameterType),
1266 typeParams, docTypeParams);
1267 if (paramType != (string) member.Parameters [i]) {
1272 if (!good) continue;
1280 private static IEnumerable<IMemberReference> GetReflectionMembers (TypeDefinition type, string docName)
1282 // need to worry about 4 forms of //@MemberName values:
1283 // 1. "Normal" (non-generic) member names: GetEnumerator
1285 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
1286 // - try as-is, and try type.member (due to "kludge" for property
1288 // 3. "Normal" Generic member names: Sort<T> (CSC)
1289 // - need to remove generic parameters --> "Sort"
1290 // 4. Explicitly-implemented interface members for generic interfaces:
1291 // -- System.Collections.Generic.IEnumerable<T>.Current
1292 // - Try as-is, and try type.member, *keeping* the generic parameters.
1293 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
1294 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
1295 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
1296 // this as (1) or (2).
1297 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
1299 foreach (IMemberReference mi in type.GetMembers (docName))
1301 if (CountChars (docName, '.') > 0)
1302 // might be a property; try only type.member instead of
1303 // namespace.type.member.
1304 foreach (IMemberReference mi in
1305 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
1312 int startLt, startType, startMethod;
1313 startLt = startType = startMethod = -1;
1314 for (int i = 0; i < docName.Length; ++i) {
1315 switch (docName [i]) {
1324 if (numLt == 0 && (i + 1) < docName.Length)
1325 // there's another character in docName, so this <...> sequence is
1326 // probably part of a generic type -- case 4.
1330 startType = startMethod;
1336 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
1338 foreach (IMemberReference mi in type.GetMembers (refName))
1342 foreach (IMemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
1345 // If we _still_ haven't found it, we've hit another generic naming issue:
1346 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
1347 // explicitly-implemented METHOD names (not properties), e.g.
1348 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
1349 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
1350 // which the XML docs will contain.
1352 // Alas, we can't derive the Mono name from docName, so we need to iterate
1353 // over all member names, convert them into CSC format, and compare... :-(
1356 foreach (IMemberReference mi in type.GetMembers ()) {
1357 if (GetMemberName (mi) == docName)
1362 static string[] GetTypeParameters (string docName)
1364 if (docName [docName.Length-1] != '>')
1366 StringList types = new StringList ();
1367 int endToken = docName.Length-2;
1368 int i = docName.Length-2;
1370 if (docName [i] == ',' || docName [i] == '<') {
1371 types.Add (docName.Substring (i + 1, endToken - i));
1374 if (docName [i] == '<')
1379 return types.ToArray ();
1382 static string GetReplacedString (string typeName, string[] from, string[] to)
1386 for (int i = 0; i < from.Length; ++i)
1387 typeName = typeName.Replace (from [i], to [i]);
1391 // CREATE A STUB DOCUMENTATION FILE
1393 public static XmlElement StubType (TypeDefinition type, string output, XmlReader ecmaDocsType)
1395 string typesig = MakeTypeSignature(type);
1396 if (typesig == null) return null; // not publicly visible
1398 XmlDocument doc = new XmlDocument();
1399 XmlElement root = doc.CreateElement("Type");
1400 doc.AppendChild (root);
1402 DoUpdateType2 ("New Type", doc, type, output, true, ecmaDocsType);
1407 private static XmlElement CreateSinceNode (XmlDocument doc)
1409 XmlElement s = doc.CreateElement ("since");
1410 s.SetAttribute ("version", since);
1414 // STUBBING/UPDATING FUNCTIONS
1416 public static void UpdateType (XmlElement root, TypeDefinition type, XmlReader ecmaDocsType)
1418 root.SetAttribute("Name", GetDocTypeName (type));
1419 root.SetAttribute("FullName", GetDocTypeFullName (type));
1421 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Language", "C#");
1422 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Value", MakeTypeSignature(type));
1424 XmlElement ass = WriteElement(root, "AssemblyInfo");
1425 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1426 if (!no_assembly_versions) {
1427 UpdateAssemblyVersions (root, type, true);
1430 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1431 foreach (var version in versions)
1432 ass.RemoveChild (version);
1434 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1435 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1437 ClearElement(ass, "AssemblyCulture");
1439 // Why-oh-why do we put assembly attributes in each type file?
1440 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1441 // since they're outdated in current docs, and a waste of space.
1442 //MakeAttributes(ass, type.Assembly, true);
1443 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1444 if (assattrs != null)
1445 ass.RemoveChild(assattrs);
1447 NormalizeWhitespace(ass);
1449 if (type.IsGenericType ()) {
1450 MakeTypeParameters (root, type.GenericParameters);
1452 ClearElement(root, "TypeParameters");
1455 if (type.BaseType != null) {
1456 XmlElement basenode = WriteElement(root, "Base");
1458 string basetypename = GetDocTypeFullName (type.BaseType);
1459 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1460 WriteElementText(root, "Base/BaseTypeName", basetypename);
1462 // Document how this type instantiates the generic parameters of its base type
1463 TypeReference origBase = type.BaseType.GetOriginalType ();
1464 if (origBase.IsGenericType ()) {
1465 ClearElement(basenode, "BaseTypeArguments");
1466 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1467 GenericArgumentCollection baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1468 GenericParameterCollection baseGenParams = origBase.GenericParameters;
1469 if (baseGenArgs.Count != baseGenParams.Count)
1470 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1471 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1472 GenericParameter param = baseGenParams [i];
1473 TypeReference value = baseGenArgs [i];
1475 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1476 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1477 bta.AppendChild(arg);
1478 arg.SetAttribute ("TypeParamName", param.Name);
1479 arg.InnerText = GetDocTypeFullName (value);
1483 ClearElement(root, "Base");
1486 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1487 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1488 List<string> interface_names = userInterfaces
1489 .Select (iface => GetDocTypeFullName (iface))
1493 XmlElement interfaces = WriteElement(root, "Interfaces");
1494 interfaces.RemoveAll();
1495 foreach (string iname in interface_names) {
1496 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1497 interfaces.AppendChild(iface);
1498 WriteElementText(iface, "InterfaceName", iname);
1501 ClearElement(root, "Interfaces");
1504 MakeAttributes (root, type.CustomAttributes, false);
1506 if (DocUtils.IsDelegate (type)) {
1507 MakeTypeParameters (root, type.GenericParameters);
1508 MakeParameters(root, type.GetMethod("Invoke").Parameters);
1509 MakeReturnValue(root, type.GetMethod("Invoke"));
1512 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1513 if (ecmaDocsType != null) {
1514 if (ecmaDocsType.Name != "Docs") {
1515 int depth = ecmaDocsType.Depth;
1516 while (ecmaDocsType.Read ()) {
1517 if (ecmaDocsType.Name == "Docs" && ecmaDocsType.Depth == depth + 1)
1521 if (!ecmaDocsType.IsStartElement ("Docs"))
1522 throw new InvalidOperationException ("Found " + ecmaDocsType.Name + "; expecting <Docs/>!");
1523 typeInfo.EcmaDocs = ecmaDocsType;
1525 MakeDocNode (typeInfo);
1527 if (!DocUtils.IsDelegate (type))
1528 WriteElement (root, "Members");
1530 NormalizeWhitespace(root);
1533 static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1535 List<T> l = new List<T> (list);
1540 private static void UpdateMember (DocsNodeInfo info)
1542 XmlElement me = (XmlElement) info.Node;
1543 IMemberReference mi = info.Member;
1544 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Language", "C#");
1545 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Value", MakeMemberSignature(mi));
1547 WriteElementText(me, "MemberType", GetMemberType(mi));
1549 if (!no_assembly_versions) {
1550 UpdateAssemblyVersions (me, mi, true);
1553 ClearElement (me, "AssemblyInfo");
1555 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1557 MakeAttributes (me, p.CustomAttributes, false);
1558 MakeReturnValue(me, mi);
1559 if (mi is MethodReference) {
1560 MethodReference mb = (MethodReference) mi;
1561 if (mb.IsGenericMethod ())
1562 MakeTypeParameters (me, mb.GenericParameters);
1564 MakeParameters(me, mi);
1567 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1568 WriteElementText(me, "MemberValue", fieldValue);
1570 info.Node = WriteElement (me, "Docs");
1572 UpdateExtensionMethods (me, info);
1575 static readonly string[] ValidExtensionMembers = {
1584 static readonly string[] ValidExtensionDocMembers = {
1590 private static void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1592 MethodDefinition me = info.Member as MethodDefinition;
1595 if (info.Parameters.Count < 1)
1597 if (!DocUtils.IsExtensionMethod (me))
1600 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1601 XmlNode member = e.CloneNode (true);
1602 em.AppendChild (member);
1603 RemoveExcept (member, ValidExtensionMembers);
1604 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1605 WriteElementText (member, "MemberType", "ExtensionMethod");
1606 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1607 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1608 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1609 member.AppendChild (link);
1610 AddTargets (em, info);
1612 extensionMethods.Add (em);
1615 private static void RemoveExcept (XmlNode node, string[] except)
1619 MyXmlNodeList remove = null;
1620 foreach (XmlNode n in node.ChildNodes) {
1621 if (Array.BinarySearch (except, n.Name) < 0) {
1623 remove = new MyXmlNodeList ();
1628 foreach (XmlNode n in remove)
1629 node.RemoveChild (n);
1632 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1634 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1635 member.PrependChild (targets);
1636 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1637 AppendElementAttributeText (targets, "Target", "Type",
1638 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1641 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1642 ConstraintCollection constraints = gp.Constraints;
1643 if (constraints.Count == 0)
1644 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1646 foreach (TypeReference c in constraints)
1647 AppendElementAttributeText(targets, "Target", "Type",
1648 slashdocFormatter.GetDeclaration (c));
1652 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1655 TypeDefinition type = DocUtils.GetTypeDefinition (field.DeclaringType);
1656 if (type != null && type.IsEnum) return false;
1658 if (type != null && type.IsGenericType ()) return false;
1659 if (!field.HasConstant)
1661 if (field.IsLiteral) {
1662 object val = field.Constant;
1663 if (val == null) value = "null";
1664 else if (val is Enum) value = val.ToString();
1665 else if (val is IFormattable) {
1666 value = ((IFormattable)val).ToString();
1668 value = "\"" + value + "\"";
1670 if (value != null && value != "")
1676 // XML HELPER FUNCTIONS
1678 private static XmlElement WriteElement(XmlNode parent, string element) {
1679 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1681 string[] path = element.Split('/');
1682 foreach (string p in path) {
1683 ret = (XmlElement)parent.SelectSingleNode(p);
1686 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1687 ename = ename.Substring(0, ename.IndexOf('['));
1688 ret = parent.OwnerDocument.CreateElement(ename);
1689 parent.AppendChild(ret);
1698 private static void WriteElementText(XmlNode parent, string element, string value) {
1699 XmlElement node = WriteElement(parent, element);
1700 node.InnerText = value;
1703 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1705 XmlElement n = parent.OwnerDocument.CreateElement (element);
1706 parent.AppendChild (n);
1707 n.InnerText = value;
1711 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1713 XmlElement n = parent.OwnerDocument.CreateElement (element);
1714 parent.AppendChild (n);
1715 n.SetAttribute (attribute, value);
1719 private static XmlNode CopyNode (XmlNode source, XmlNode dest)
1721 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1722 dest.AppendChild (copy);
1726 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1727 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1730 node = WriteElement(parent, element);
1731 node.InnerText = value;
1733 private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
1734 XmlElement node = WriteElement(parent, element);
1735 if (node.GetAttribute(attribute) == value) return;
1736 node.SetAttribute(attribute, value);
1738 private static void ClearElement(XmlElement parent, string name) {
1739 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1741 parent.RemoveChild(node);
1744 // DOCUMENTATION HELPER FUNCTIONS
1746 private static void MakeDocNode (DocsNodeInfo info)
1748 List<GenericParameter> genericParams = info.GenericParameters;
1749 ParameterDefinitionCollection parameters = info.Parameters;
1750 TypeReference returntype = info.ReturnType;
1751 bool returnisreturn = info.ReturnIsReturn;
1752 XmlElement e = info.Node;
1753 bool addremarks = info.AddRemarks;
1755 WriteElementInitialText(e, "summary", "To be added.");
1757 if (parameters != null) {
1758 string[] values = new string [parameters.Count];
1759 for (int i = 0; i < values.Length; ++i)
1760 values [i] = parameters [i].Name;
1761 UpdateParameters (e, "param", values);
1764 if (genericParams != null) {
1765 string[] values = new string [genericParams.Count];
1766 for (int i = 0; i < values.Length; ++i)
1767 values [i] = genericParams [i].Name;
1768 UpdateParameters (e, "typeparam", values);
1771 string retnodename = null;
1772 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
1773 retnodename = returnisreturn ? "returns" : "value";
1774 string retnodename_other = !returnisreturn ? "returns" : "value";
1776 // If it has a returns node instead of a value node, change its name.
1777 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1778 if (retother != null) {
1779 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1780 foreach (XmlNode node in retother)
1781 retnode.AppendChild(node.CloneNode(true));
1782 e.ReplaceChild(retnode, retother);
1784 WriteElementInitialText(e, retnodename, "To be added.");
1787 ClearElement(e, "returns");
1788 ClearElement(e, "value");
1792 WriteElementInitialText(e, "remarks", "To be added.");
1794 if (exceptions.HasValue && info.Member != null) {
1795 UpdateExceptions (e, info.Member);
1798 if (info.EcmaDocs != null) {
1799 XmlReader r = info.EcmaDocs;
1800 int depth = r.Depth;
1801 r.ReadStartElement ("Docs");
1803 if (r.Name == "Docs") {
1804 if (r.Depth == depth && r.NodeType == XmlNodeType.EndElement)
1807 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
1809 if (!r.IsStartElement ())
1814 XmlNode doc = e.SelectSingleNode (
1815 r.Name + "[@name='" + r.GetAttribute ("name") + "']");
1816 string value = r.ReadInnerXml ();
1818 doc.InnerXml = value.Replace ("\r", "");
1825 string name = r.Name;
1826 string cref = r.GetAttribute ("cref");
1827 XmlNode doc = e.SelectSingleNode (
1828 r.Name + "[@cref='" + cref + "']");
1829 string value = r.ReadInnerXml ().Replace ("\r", "");
1831 doc.InnerXml = value;
1833 XmlElement n = e.OwnerDocument.CreateElement (name);
1834 n.SetAttribute ("cref", cref);
1841 string name = r.Name;
1842 string xpath = r.Name;
1843 StringList attributes = new StringList (r.AttributeCount);
1844 if (r.MoveToFirstAttribute ()) {
1846 attributes.Add ("@" + r.Name + "=\"" + r.Value + "\"");
1847 } while (r.MoveToNextAttribute ());
1850 if (attributes.Count > 0) {
1851 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
1853 XmlNode doc = e.SelectSingleNode (xpath);
1854 string value = r.ReadInnerXml ().Replace ("\r", "");
1856 doc.InnerXml = value;
1859 XmlElement n = e.OwnerDocument.CreateElement (name);
1861 foreach (string a in attributes) {
1862 int eq = a.IndexOf ('=');
1863 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
1872 if (info.SlashDocs != null) {
1873 XmlNode elem = info.SlashDocs;
1875 if (elem.SelectSingleNode("summary") != null)
1876 ClearElement(e, "summary");
1877 if (elem.SelectSingleNode("remarks") != null)
1878 ClearElement(e, "remarks");
1879 if (elem.SelectSingleNode("value") != null)
1880 ClearElement(e, "value");
1881 if (retnodename != null && elem.SelectSingleNode(retnodename) != null)
1882 ClearElement(e, retnodename);
1884 foreach (XmlNode child in elem.ChildNodes) {
1885 switch (child.Name) {
1888 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + child.Attributes ["name"].Value + "']");
1890 p2.InnerXml = child.InnerXml;
1895 case "permission": {
1896 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + child.Attributes ["cref"].Value + "']");
1898 a = e.OwnerDocument.CreateElement (child.Name);
1899 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
1902 a.InnerXml = child.InnerXml;
1906 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + child.Attributes ["cref"].Value + "']");
1908 a = e.OwnerDocument.CreateElement ("altmember");
1909 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
1915 CopyNode (child, e);
1922 OrderDocsNodes (e, e.ChildNodes);
1923 NormalizeWhitespace(e);
1926 static readonly string[] DocsNodeOrder = {
1927 "typeparam", "param", "summary", "returns", "value", "remarks",
1930 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
1932 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1933 for (int i = 0; i < DocsNodeOrder.Length; ++i) {
1934 for (int j = 0; j < children.Count; ++j) {
1935 XmlNode c = children [j];
1936 if (c.Name == DocsNodeOrder [i]) {
1937 newChildren.Add (c);
1941 if (newChildren.Count >= 0)
1942 docs.PrependChild ((XmlNode) newChildren [0]);
1943 for (int i = 1; i < newChildren.Count; ++i) {
1944 XmlNode prev = (XmlNode) newChildren [i-1];
1945 XmlNode cur = (XmlNode) newChildren [i];
1946 docs.RemoveChild (cur);
1947 docs.InsertAfter (cur, prev);
1952 private static void UpdateParameters (XmlElement e, string element, string[] values)
1954 if (values != null) {
1955 XmlNode[] paramnodes = new XmlNode[values.Length];
1957 // Some documentation had param nodes with leading spaces.
1958 foreach (XmlElement paramnode in e.SelectNodes(element)){
1959 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
1962 // If a member has only one parameter, we can track changes to
1963 // the name of the parameter easily.
1964 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
1965 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
1968 bool reinsert = false;
1970 // Pick out existing and still-valid param nodes, and
1971 // create nodes for parameters not in the file.
1972 Hashtable seenParams = new Hashtable();
1973 for (int pi = 0; pi < values.Length; pi++) {
1974 string p = values [pi];
1977 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
1978 if (paramnodes[pi] != null) continue;
1980 XmlElement pe = e.OwnerDocument.CreateElement(element);
1981 pe.SetAttribute("name", p);
1982 pe.InnerText = "To be added.";
1983 paramnodes[pi] = pe;
1987 // Remove parameters that no longer exist and check all params are in the right order.
1989 MyXmlNodeList todelete = new MyXmlNodeList ();
1990 foreach (XmlElement paramnode in e.SelectNodes(element)) {
1991 string name = paramnode.GetAttribute("name");
1992 if (!seenParams.ContainsKey(name)) {
1993 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1994 Error ("The following param node can only be deleted if the --delete option is given: ");
1995 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
1997 Error ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
1998 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2002 Error ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
2003 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2004 e.ParentNode.Attributes ["MemberName"].Value,
2007 Error ("\tValue={0}", paramnode.OuterXml);
2009 todelete.Add (paramnode);
2014 if ((int)seenParams[name] != idx)
2020 foreach (XmlNode n in todelete) {
2021 n.ParentNode.RemoveChild (n);
2024 // Re-insert the parameter nodes at the top of the doc section.
2026 for (int pi = values.Length-1; pi >= 0; pi--)
2027 e.PrependChild(paramnodes[pi]);
2029 // Clear all existing param nodes
2030 foreach (XmlNode paramnode in e.SelectNodes(element)) {
2031 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2032 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
2033 Console.WriteLine(paramnode.OuterXml);
2035 paramnode.ParentNode.RemoveChild(paramnode);
2041 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
2043 string existingName = pe.GetAttribute ("name");
2044 pe.SetAttribute ("name", newName);
2045 if (existingName == newName)
2047 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
2048 if (paramref.GetAttribute ("name").Trim () == existingName)
2049 paramref.SetAttribute ("name", newName);
2052 class CrefComparer : XmlNodeComparer {
2054 public CrefComparer ()
2058 public override int Compare (XmlNode x, XmlNode y)
2060 string xType = x.Attributes ["cref"].Value;
2061 string yType = y.Attributes ["cref"].Value;
2062 string xNamespace = GetNamespace (xType);
2063 string yNamespace = GetNamespace (yType);
2065 int c = xNamespace.CompareTo (yNamespace);
2068 return xType.CompareTo (yType);
2071 static string GetNamespace (string type)
2073 int n = type.LastIndexOf ('.');
2075 return type.Substring (0, n);
2076 return string.Empty;
2080 private static void UpdateExceptions (XmlNode docs, IMemberReference member)
2082 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
2083 string cref = slashdocFormatter.GetDeclaration (source.Exception);
2084 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
2087 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
2088 e.SetAttribute ("cref", cref);
2089 e.InnerXml = "To be added; from: <see cref=\"" +
2090 string.Join ("\" />, <see cref=\"",
2091 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
2094 docs.AppendChild (e);
2096 SortXmlNodes (docs, docs.SelectNodes ("exception"),
2097 new CrefComparer ());
2100 private static void NormalizeWhitespace(XmlElement e) {
2101 // Remove all text and whitespace nodes from the element so it
2102 // is outputted with nice indentation and no blank lines.
2103 ArrayList deleteNodes = new ArrayList();
2104 foreach (XmlNode n in e)
2105 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2107 foreach (XmlNode n in deleteNodes)
2108 n.ParentNode.RemoveChild(n);
2111 private static bool UpdateAssemblyVersions (XmlElement root, IMemberReference member, bool add)
2113 TypeDefinition type = member as TypeDefinition;
2115 type = member.DeclaringType as TypeDefinition;
2116 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
2119 private static string GetAssemblyVersion (AssemblyDefinition assembly)
2121 return assembly.Name.Version.ToString();
2124 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
2126 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
2128 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2129 root.AppendChild(e);
2131 MyXmlNodeList matches = new MyXmlNodeList (assemblyVersions.Length);
2132 foreach (XmlElement v in e.SelectNodes ("AssemblyVersion")) {
2133 foreach (string sv in assemblyVersions)
2134 if (v.InnerText == sv)
2137 // matches.Count > 0 && add: ignore -- already present
2138 if (matches.Count > 0 && !add) {
2139 foreach (XmlNode c in matches)
2142 else if (matches.Count == 0 && add) {
2143 foreach (string sv in assemblyVersions) {
2144 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2149 // matches.Count == 0 && !add: ignore -- already not present
2151 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2152 SortXmlNodes (e, avs, new VersionComparer ());
2154 return avs.Count != 0;
2157 // FIXME: get TypeReferences instead of string comparison?
2158 private static string[] IgnorableAttributes = {
2159 // Security related attributes
2160 "System.Reflection.AssemblyKeyFileAttribute",
2161 "System.Reflection.AssemblyDelaySignAttribute",
2162 // Present in @RefType
2163 "System.Runtime.InteropServices.OutAttribute",
2164 // For naming the indexer to use when not using indexers
2165 "System.Reflection.DefaultMemberAttribute",
2166 // for decimal constants
2167 "System.Runtime.CompilerServices.DecimalConstantAttribute",
2168 // compiler generated code
2169 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
2170 // more compiler generated code, e.g. iterator methods
2171 "System.Diagnostics.DebuggerHiddenAttribute",
2172 "System.Runtime.CompilerServices.FixedBufferAttribute",
2173 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
2174 // extension methods
2175 "System.Runtime.CompilerServices.ExtensionAttribute",
2178 private static void MakeAttributes (XmlElement root, CustomAttributeCollection attributes, bool assemblyAttributes)
2180 if (attributes.Count == 0) {
2181 ClearElement(root, "Attributes");
2186 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2190 e = root.OwnerDocument.CreateElement("Attributes");
2192 foreach (CustomAttribute attribute in attributes.Cast<CustomAttribute> ()
2193 .OrderBy (ca => ca.Constructor.DeclaringType.FullName)) {
2194 if (!attribute.Resolve ()) {
2196 Error ("warning: could not resolve type {0}.",
2197 attribute.Constructor.DeclaringType.FullName);
2199 TypeDefinition attrType = attribute.Constructor.DeclaringType as TypeDefinition;
2200 if (attrType != null && !IsPublic (attrType))
2202 if (slashdocFormatter.GetName (attribute.Constructor.DeclaringType) == null)
2205 if (Array.IndexOf (IgnorableAttributes, attribute.Constructor.DeclaringType.FullName) >= 0)
2210 StringList fields = new StringList ();
2212 ParameterDefinitionCollection parameters = attribute.Constructor.Parameters;
2213 for (int i = 0; i < attribute.ConstructorParameters.Count; ++i) {
2214 fields.Add (MakeAttributesValueString (
2215 attribute.ConstructorParameters [i],
2216 parameters [i].ParameterType));
2219 (from de in attribute.Fields.Cast<DictionaryEntry> ()
2220 select new { Type=attribute.GetFieldType (de.Key.ToString ()), Name=de.Key, Value=de.Value })
2222 (from de in attribute.Properties.Cast<DictionaryEntry> ()
2223 select new { Type=attribute.GetPropertyType (de.Key.ToString ()), Name=de.Key, Value=de.Value }))
2224 .OrderBy (v => v.Name);
2225 foreach (var d in namedArgs)
2226 fields.Add (string.Format ("{0}={1}", d.Name,
2227 MakeAttributesValueString (d.Value, d.Type)));
2229 string a2 = String.Join(", ", fields.ToArray ());
2230 if (a2 != "") a2 = "(" + a2 + ")";
2232 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2235 string name = attribute.Constructor.DeclaringType.FullName;
2236 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
2237 WriteElementText(ae, "AttributeName", name + a2);
2240 if (b && e.ParentNode == null)
2241 root.AppendChild(e);
2243 ClearElement(root, "Attributes");
2245 NormalizeWhitespace(e);
2248 private static string MakeAttributesValueString (object v, TypeReference valueType)
2252 if (valueType.FullName == "System.Type")
2253 return "typeof(" + v.ToString () + ")";
2254 if (valueType.FullName == "System.String")
2255 return "\"" + v.ToString () + "\"";
2257 return (bool)v ? "true" : "false";
2258 TypeDefinition valueDef = DocUtils.GetTypeDefinition (valueType);
2259 if (valueDef == null || !valueDef.IsEnum)
2260 return v.ToString ();
2261 string typename = GetDocTypeFullName (valueType);
2262 var values = GetEnumerationValues (valueDef);
2263 ulong c = Convert.ToUInt64 (v);
2264 if (values.ContainsKey (c))
2265 return typename + "." + values [c];
2266 if (valueDef.CustomAttributes.Cast<CustomAttribute> ()
2267 .Any (ca => ca.Constructor.DeclaringType.FullName == "System.FlagsAttribute")) {
2268 return string.Join (" | ",
2269 (from i in values.Keys
2271 select typename + "." + values [i])
2274 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
2277 private static Dictionary<ulong, string> GetEnumerationValues (TypeDefinition type)
2280 (from f in type.Fields.Cast<FieldDefinition> ()
2281 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
2283 .ToDictionary (f => Convert.ToUInt64 (f.Constant), f => f.Name);
2286 private static void MakeParameters (XmlElement root, ParameterDefinitionCollection parameters)
2288 XmlElement e = WriteElement(root, "Parameters");
2290 foreach (ParameterDefinition p in parameters) {
2291 XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
2293 pe.SetAttribute("Name", p.Name);
2294 pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
2295 if (p.ParameterType is ReferenceType) {
2296 if (p.IsOut) pe.SetAttribute("RefType", "out");
2297 else pe.SetAttribute("RefType", "ref");
2299 MakeAttributes (pe, p.CustomAttributes, false);
2303 private static void MakeTypeParameters (XmlElement root, GenericParameterCollection typeParams)
2305 if (typeParams == null || typeParams.Count == 0) {
2306 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2308 root.RemoveChild (f);
2311 XmlElement e = WriteElement(root, "TypeParameters");
2313 foreach (GenericParameter t in typeParams) {
2314 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2316 pe.SetAttribute("Name", t.Name);
2317 MakeAttributes (pe, t.CustomAttributes, false);
2318 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2319 ConstraintCollection constraints = t.Constraints;
2320 GenericParameterAttributes attrs = t.Attributes;
2321 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2329 ce = root.OwnerDocument.CreateElement ("Constraints");
2331 pe.AppendChild (ce);
2332 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2333 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2334 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2335 AppendElementText (ce, "ParameterAttribute", "Covariant");
2336 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2337 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2338 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2339 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2340 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2341 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2342 foreach (TypeReference c in constraints) {
2343 TypeDefinition cd = DocUtils.GetTypeDefinition (c);
2344 AppendElementText (ce,
2345 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2346 GetDocTypeFullName (c));
2351 private static void MakeParameters (XmlElement root, IMemberReference mi)
2353 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2354 MakeParameters (root, ((MethodDefinition)mi).Parameters);
2355 else if (mi is MethodDefinition) {
2356 MethodDefinition mb = (MethodDefinition) mi;
2357 ParameterDefinitionCollection parameters = mb.Parameters;
2358 MakeParameters(root, parameters);
2359 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2360 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2361 p.SetAttribute ("RefType", "this");
2364 else if (mi is PropertyDefinition) {
2365 ParameterDefinitionCollection parameters = ((PropertyDefinition)mi).Parameters;
2366 if (parameters.Count > 0)
2367 MakeParameters(root, parameters);
2371 else if (mi is FieldDefinition) return;
2372 else if (mi is EventDefinition) return;
2373 else throw new ArgumentException();
2376 private static string GetDocParameterType (TypeReference type)
2378 return GetDocTypeFullName (type).Replace ("@", "&");
2381 private static void MakeReturnValue(XmlElement root, TypeReference type, CustomAttributeCollection attributes)
2383 XmlElement e = WriteElement(root, "ReturnValue");
2385 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2386 if (attributes != null)
2387 MakeAttributes(e, attributes, false);
2390 private static void MakeReturnValue (XmlElement root, IMemberReference mi)
2392 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2394 else if (mi is MethodDefinition)
2395 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType.ReturnType, ((MethodDefinition)mi).ReturnType.CustomAttributes);
2396 else if (mi is PropertyDefinition)
2397 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
2398 else if (mi is FieldDefinition)
2399 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
2400 else if (mi is EventDefinition)
2401 MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
2403 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2406 private static XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info) {
2407 IMemberReference mi = info.Member;
2408 if (mi is TypeDefinition) return null;
2410 string sigs = MakeMemberSignature(mi);
2411 if (sigs == null) return null; // not publicly visible
2413 // no documentation for property/event accessors. Is there a better way of doing this?
2414 if (mi.Name.StartsWith("get_")) return null;
2415 if (mi.Name.StartsWith("set_")) return null;
2416 if (mi.Name.StartsWith("add_")) return null;
2417 if (mi.Name.StartsWith("remove_")) return null;
2418 if (mi.Name.StartsWith("raise_")) return null;
2420 XmlElement me = doc.CreateElement("Member");
2421 me.SetAttribute("MemberName", GetMemberName (mi));
2426 if (since != null) {
2427 XmlNode docs = me.SelectSingleNode("Docs");
2428 docs.AppendChild (CreateSinceNode (doc));
2434 private static string GetMemberName (IMemberReference mi)
2436 MethodDefinition mb = mi as MethodDefinition;
2438 PropertyDefinition pi = mi as PropertyDefinition;
2441 return DocUtils.GetPropertyName (pi);
2443 StringBuilder sb = new StringBuilder (mi.Name.Length);
2444 if (!DocUtils.IsExplicitlyImplemented (mb))
2445 sb.Append (mi.Name);
2447 TypeReference iface;
2448 MethodReference ifaceMethod;
2449 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2450 sb.Append (GetDocTypeFullName (iface));
2452 sb.Append (ifaceMethod.Name);
2454 if (mb.IsGenericMethod ()) {
2455 GenericParameterCollection typeParams = mb.GenericParameters;
2456 if (typeParams.Count > 0) {
2458 sb.Append (typeParams [0].Name);
2459 for (int i = 1; i < typeParams.Count; ++i)
2460 sb.Append (",").Append (typeParams [i].Name);
2464 return sb.ToString ();
2467 private static int CountChars (string s, char c)
2470 for (int i = 0; i < s.Length; ++i) {
2477 /// SIGNATURE GENERATION FUNCTIONS
2479 static string MakeTypeSignature (TypeReference type)
2481 return csharpFormatter.GetDeclaration (type);
2484 static string MakeMemberSignature (IMemberReference mi)
2486 return csharpFullFormatter.GetDeclaration (mi);
2489 static string GetMemberType (IMemberReference mi)
2491 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2492 return "Constructor";
2493 if (mi is MethodDefinition)
2495 if (mi is PropertyDefinition)
2497 if (mi is FieldDefinition)
2499 if (mi is EventDefinition)
2501 throw new ArgumentException();
2504 private static string GetDocTypeName (TypeReference type)
2506 return docTypeFormatter.GetName (type);
2509 private static string GetDocTypeFullName (TypeReference type)
2511 return DocTypeFullMemberFormatter.Default.GetName (type);
2514 class DocsNodeInfo {
2515 public DocsNodeInfo (XmlElement node)
2520 public DocsNodeInfo (XmlElement node, TypeDefinition type)
2526 public DocsNodeInfo (XmlElement node, IMemberReference member)
2529 SetMemberInfo (member);
2532 public void SetType (TypeDefinition type)
2535 throw new ArgumentNullException ("type");
2536 GenericParameters = new List<GenericParameter> (type.GenericParameters.Cast<GenericParameter> ());
2537 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
2538 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
2539 for (int i = 0; i < declTypes.Count - 1; ++i) {
2540 int remove = System.Math.Min (maxGenArgs,
2541 DocUtils.GetGenericArgumentCount (declTypes [i]));
2542 maxGenArgs -= remove;
2543 while (remove-- > 0)
2544 GenericParameters.RemoveAt (0);
2546 if (DocUtils.IsDelegate (type)) {
2547 Parameters = type.GetMethod("Invoke").Parameters;
2548 ReturnType = type.GetMethod("Invoke").ReturnType.ReturnType;
2550 SetSlashDocs (type);
2553 public void SetMemberInfo (IMemberReference member)
2556 throw new ArgumentNullException ("member");
2557 ReturnIsReturn = true;
2561 if (member is MethodReference ) {
2562 MethodReference mr = (MethodReference) member;
2563 Parameters = mr.Parameters;
2564 if (mr.IsGenericMethod ()) {
2565 GenericParameters = new List<GenericParameter> (mr.GenericParameters.Cast<GenericParameter> ());
2568 else if (member is PropertyDefinition) {
2569 Parameters = ((PropertyDefinition) member).Parameters;
2572 if (member is MethodDefinition) {
2573 ReturnType = ((MethodDefinition) member).ReturnType.ReturnType;
2574 } else if (member is PropertyDefinition) {
2575 ReturnType = ((PropertyDefinition) member).PropertyType;
2576 ReturnIsReturn = false;
2579 // no remarks section for enum members
2580 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
2582 SetSlashDocs (member);
2585 private void SetSlashDocs (IMemberReference member)
2587 if (slashdocs == null)
2590 string slashdocsig = slashdocFormatter.GetDeclaration (member);
2591 if (slashdocsig != null)
2592 SlashDocs = slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
2595 public TypeReference ReturnType;
2596 public List<GenericParameter> GenericParameters;
2597 public ParameterDefinitionCollection Parameters;
2598 public bool ReturnIsReturn;
2599 public XmlElement Node;
2600 public bool AddRemarks = true;
2601 public XmlNode SlashDocs;
2602 public XmlReader EcmaDocs;
2603 public IMemberReference Member;
2606 static string GetXPathForMember (DocumentationMember member)
2608 StringBuilder xpath = new StringBuilder ();
2609 xpath.Append ("//Members/Member[@MemberName=\"")
2610 .Append (member.MemberName)
2612 if (member.Parameters != null && member.Parameters.Count > 0) {
2613 xpath.Append ("/Parameters[count(Parameter) = ")
2614 .Append (member.Parameters.Count);
2615 for (int i = 0; i < member.Parameters.Count; ++i) {
2616 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2617 xpath.Append (member.Parameters [i]);
2618 xpath.Append ("\"");
2620 xpath.Append ("]/..");
2622 return xpath.ToString ();
2625 public static string GetXPathForMember (XPathNavigator member)
2627 StringBuilder xpath = new StringBuilder ();
2628 xpath.Append ("//Type[@FullName=\"")
2629 .Append (member.SelectSingleNode ("../../@FullName").Value)
2631 xpath.Append ("Members/Member[@MemberName=\"")
2632 .Append (member.SelectSingleNode ("@MemberName").Value)
2634 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2635 if (parameters.Count > 0) {
2636 xpath.Append ("/Parameters[count(Parameter) = ")
2637 .Append (parameters.Count);
2639 while (parameters.MoveNext ()) {
2641 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2642 xpath.Append (parameters.Current.Value);
2643 xpath.Append ("\"");
2645 xpath.Append ("]/..");
2647 return xpath.ToString ();
2650 public static string GetXPathForMember (IMemberReference member)
2652 StringBuilder xpath = new StringBuilder ();
2653 xpath.Append ("//Type[@FullName=\"")
2654 .Append (member.DeclaringType.FullName)
2656 xpath.Append ("Members/Member[@MemberName=\"")
2657 .Append (GetMemberName (member))
2660 ParameterDefinitionCollection parameters = null;
2661 if (member is MethodDefinition)
2662 parameters = ((MethodDefinition) member).Parameters;
2663 else if (member is PropertyDefinition) {
2664 parameters = ((PropertyDefinition) member).Parameters;
2666 if (parameters != null && parameters.Count > 0) {
2667 xpath.Append ("/Parameters[count(Parameter) = ")
2668 .Append (parameters.Count);
2669 for (int i = 0; i < parameters.Count; ++i) {
2670 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2671 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2672 xpath.Append ("\"");
2674 xpath.Append ("]/..");
2676 return xpath.ToString ();
2680 static class CecilExtensions {
2681 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type)
2683 foreach (var c in type.Constructors)
2684 yield return (IMemberReference) c;
2685 foreach (var e in type.Events)
2686 yield return (IMemberReference) e;
2687 foreach (var f in type.Fields)
2688 yield return (IMemberReference) f;
2689 foreach (var m in type.Methods)
2690 yield return (IMemberReference) m;
2691 foreach (var t in type.NestedTypes)
2692 yield return (IMemberReference) t;
2693 foreach (var p in type.Properties)
2694 yield return (IMemberReference) p;
2697 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type, string member)
2699 return GetMembers (type).Where (m => m.Name == member);
2702 public static IMemberReference GetMember (this TypeDefinition type, string member)
2704 return GetMembers (type, member).EnsureZeroOrOne ();
2707 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2709 if (source.Count () > 1)
2710 throw new InvalidOperationException ("too many matches");
2711 return source.FirstOrDefault ();
2714 static T EnsureOne<T> (this IEnumerable<T> source)
2716 if (source.Count () > 1)
2717 throw new InvalidOperationException ("too many matches: " +
2718 string.Join ("; ", source.Select (e => e.ToString ()).ToArray ()));
2719 return source.First ();
2722 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2724 return type.Methods.Cast<MethodDefinition> ()
2725 .Where (m => m.Name == method)
2726 .EnsureZeroOrOne ();
2729 public static IEnumerable<IMemberReference> GetDefaultMembers (this TypeReference type)
2731 TypeDefinition def = type as TypeDefinition;
2733 return new IMemberReference [0];
2734 CustomAttribute defMemberAttr = type.CustomAttributes.Cast<CustomAttribute> ()
2735 .Where (c => c.Constructor.DeclaringType.FullName == "System.Reflection.DefaultMemberAttribute")
2737 if (defMemberAttr == null)
2738 return new IMemberReference [0];
2739 string name = (string) defMemberAttr.ConstructorParameters [0];
2740 return def.Properties.Cast<PropertyDefinition> ()
2741 .Where (p => p.Name == name)
2742 .Select (p => (IMemberReference) p);
2745 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2747 return assembly.Modules.Cast<ModuleDefinition> ()
2748 .SelectMany (md => md.Types.Cast<TypeDefinition> ());
2751 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2753 return GetTypes (assembly)
2754 .Where (td => td.FullName == type)
2755 .EnsureZeroOrOne ();
2758 public static bool IsGenericType (this TypeReference type)
2760 return type.GenericParameters.Count > 0;
2763 public static bool IsGenericMethod (this MethodReference method)
2765 return method.GenericParameters.Count > 0;
2768 public static IMemberReference GetDefinition (this IMemberReference member)
2770 EventReference er = member as EventReference;
2772 return GetEventDefinition (er);
2773 FieldReference fr = member as FieldReference;
2775 return GetFieldDefinition (fr);
2776 MethodReference mr = member as MethodReference;
2778 return GetMethodDefinition (mr);
2779 PropertyReference pr = member as PropertyReference;
2781 return GetPropertyDefinition (pr);
2782 TypeReference tr = member as TypeReference;
2784 return GetTypeDefinition (tr);
2785 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2788 public static EventDefinition GetEventDefinition (this EventReference ev)
2790 EventDefinition evDef = ev as EventDefinition;
2793 return (EventDefinition) ev.DeclaringType.GetTypeDefinition ()
2794 .GetMember (ev.Name);
2797 public static FieldDefinition GetFieldDefinition (this FieldReference field)
2799 FieldDefinition fieldDef = field as FieldDefinition;
2800 if (fieldDef != null)
2802 return (FieldDefinition) field.DeclaringType.GetTypeDefinition ()
2803 .GetMember (field.Name);
2806 public static MethodDefinition GetMethodDefinition (this MethodReference method)
2808 MethodDefinition methodDef = method as MethodDefinition;
2809 if (methodDef != null)
2811 method = method.GetOriginalMethod ();
2812 return method.DeclaringType.GetTypeDefinition ()
2813 .GetMembers (method.Name).OfType<MethodDefinition> ()
2815 AreSame (method.ReturnType.ReturnType, m.ReturnType.ReturnType) &&
2816 AreSame (method.Parameters, m.Parameters))
2820 static bool AreSame (ParameterDefinitionCollection a, ParameterDefinitionCollection b)
2822 if (a.Count != b.Count)
2828 for (int i = 0; i < a.Count; i++) {
2829 if (!AreSame (a [i].ParameterType, b [i].ParameterType))
2836 static bool AreSame (TypeReference a, TypeReference b)
2838 while (a is TypeSpecification || b is TypeSpecification) {
2839 if (a.GetType () != b.GetType ())
2842 IGenericInstance ga = a as IGenericInstance;
2843 IGenericInstance gb = b as IGenericInstance;
2845 a = ((TypeSpecification) a).ElementType;
2846 b = ((TypeSpecification) b).ElementType;
2848 if (ga != null && gb != null) {
2849 if (ga.GenericArguments.Count != gb.GenericArguments.Count) {
2852 for (int i = 0; i < ga.GenericArguments.Count; ++i) {
2853 if (!AreSame (ga.GenericArguments [i], gb.GenericArguments [i]))
2859 GenericParameter pa = (a as GenericParameter);
2860 GenericParameter pb = (b as GenericParameter);
2861 if ((pa != null) || (pb != null)) {
2862 if (a.GetType () != b.GetType ())
2865 return pa.Position == pb.Position;
2868 return a.FullName == b.FullName;
2871 public static PropertyDefinition GetPropertyDefinition (this PropertyReference property)
2873 PropertyDefinition propertyDef = property as PropertyDefinition;
2874 if (propertyDef != null)
2876 return (PropertyDefinition) property.DeclaringType.GetTypeDefinition ()
2877 .GetMembers (property.Name).OfType<PropertyDefinition> ()
2878 .Where (p => p.PropertyType.FullName == property.PropertyType.FullName &&
2879 AreSame (property.Parameters, p.Parameters))
2883 public static TypeDefinition GetTypeDefinition (this TypeReference type)
2885 return DocUtils.GetTypeDefinition (type);
2889 static class DocUtils {
2890 public static bool IsExplicitlyImplemented (MethodDefinition method)
2892 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2895 public static string GetTypeDotMember (string name)
2897 int startType, startMethod;
2898 startType = startMethod = -1;
2899 for (int i = 0; i < name.Length; ++i) {
2900 if (name [i] == '.') {
2901 startType = startMethod;
2905 return name.Substring (startType+1);
2908 public static string GetMember (string name)
2910 int i = name.LastIndexOf ('.');
2913 return name.Substring (i+1);
2916 public static void GetInfoForExplicitlyImplementedMethod (
2917 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2921 if (method.Overrides.Count != 1)
2922 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2923 iface = method.Overrides [0].DeclaringType;
2924 ifaceMethod = method.Overrides [0];
2927 public static string GetPropertyName (PropertyDefinition pi)
2929 // Issue: (g)mcs-generated assemblies that explicitly implement
2930 // properties don't specify the full namespace, just the
2931 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2932 MethodDefinition method = pi.GetMethod;
2934 method = pi.SetMethod;
2935 if (!IsExplicitlyImplemented (method))
2938 // Need to determine appropriate namespace for this member.
2939 TypeReference iface;
2940 MethodReference ifaceMethod;
2941 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2942 return string.Join (".", new string[]{
2943 DocTypeFullMemberFormatter.Default.GetName (iface),
2944 GetMember (pi.Name)});
2947 public static string GetNamespace (TypeReference type)
2949 if (type.GetOriginalType ().IsNested)
2950 type = type.GetOriginalType ();
2951 while (type != null && type.IsNested)
2952 type = type.DeclaringType;
2954 return string.Empty;
2955 return type.Namespace;
2958 public static string PathCombine (string dir, string path)
2964 return Path.Combine (dir, path);
2967 public static bool IsExtensionMethod (MethodDefinition method)
2970 method.CustomAttributes.Cast<CustomAttribute> ()
2971 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2973 method.DeclaringType.CustomAttributes.Cast<CustomAttribute> ()
2974 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2978 public static bool IsDelegate (TypeDefinition type)
2980 TypeReference baseRef = type.BaseType;
2981 if (baseRef == null)
2983 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2984 baseRef.FullName == "System.MulticastDelegate";
2987 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2989 List<TypeReference> decls = new List<TypeReference> ();
2991 while (type.DeclaringType != null) {
2992 decls.Add (type.DeclaringType);
2993 type = type.DeclaringType;
2999 public static int GetGenericArgumentCount (TypeReference type)
3001 GenericInstanceType inst = type as GenericInstanceType;
3003 ? inst.GenericArguments.Count
3004 : type.GenericParameters.Count;
3007 public static TypeDefinition GetTypeDefinition (TypeReference type)
3009 // Remove generic instantiation info (so string comparison below works)
3010 type = type.GetOriginalType ();
3011 TypeDefinition typeDef = type as TypeDefinition;
3012 if (typeDef != null)
3015 AssemblyNameReference reference = type.Scope as AssemblyNameReference;
3016 if (reference != null) {
3017 AssemblyDefinition ad = type.Module.Assembly.Resolver.Resolve (reference);
3018 if (ad != null && (typeDef = ad.MainModule.Types [type.FullName]) != null)
3021 ModuleDefinition module = type.Scope as ModuleDefinition;
3022 if (module != null && (typeDef = module.Types [type.FullName]) != null)
3027 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
3029 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
3030 List<TypeReference> userInterfaces = new List<TypeReference> ();
3031 foreach (TypeReference iface in type.Interfaces) {
3032 TypeReference lookup = GetTypeDefinition (iface) ?? iface;
3033 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
3034 userInterfaces.Add (iface);
3036 return userInterfaces;
3039 private static string GetQualifiedTypeName (TypeReference type)
3041 return "[" + type.Scope.Name + "]" + type.FullName;
3044 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
3046 HashSet<string> inheritedInterfaces = new HashSet<string> ();
3047 Action<TypeDefinition> a = null;
3049 if (t == null) return;
3050 foreach (TypeReference r in t.Interfaces) {
3051 inheritedInterfaces.Add (GetQualifiedTypeName (r));
3052 a (GetTypeDefinition (r));
3055 TypeReference baseRef = type.BaseType;
3056 while (baseRef != null) {
3057 TypeDefinition baseDef = GetTypeDefinition (baseRef);
3058 if (baseDef != null) {
3060 baseRef = baseDef.BaseType;
3065 foreach (TypeReference r in type.Interfaces)
3066 a (GetTypeDefinition (r));
3067 return inheritedInterfaces;
3071 class DocumentationMember {
3072 public StringToStringMap MemberSignatures = new StringToStringMap ();
3073 public string ReturnType;
3074 public StringList Parameters;
3075 public string MemberName;
3076 public string MemberType;
3078 public DocumentationMember (XmlReader reader)
3080 MemberName = reader.GetAttribute ("MemberName");
3081 int depth = reader.Depth;
3083 StringList p = new StringList ();
3085 if (reader.NodeType != XmlNodeType.Element)
3087 switch (reader.Name) {
3088 case "MemberSignature":
3089 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3092 MemberType = reader.ReadElementString ();
3095 if (reader.Depth == depth + 2)
3096 ReturnType = reader.ReadElementString ();
3099 if (reader.Depth == depth + 2)
3100 p.Add (reader.GetAttribute ("Type"));
3103 if (reader.Depth == depth + 1)
3107 } while (go && reader.Read () && reader.Depth >= depth);
3113 public DocumentationMember (XmlNode node)
3115 MemberName = node.Attributes ["MemberName"].Value;
3116 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3117 XmlAttribute l = n.Attributes ["Language"];
3118 XmlAttribute v = n.Attributes ["Value"];
3119 if (l != null && v != null)
3120 MemberSignatures [l.Value] = v.Value;
3122 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3123 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
3125 ReturnType = rt.InnerText;
3126 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
3128 Parameters = new StringList (p.Count);
3129 for (int i = 0; i < p.Count; ++i)
3130 Parameters.Add (p [i].Attributes ["Type"].Value);
3135 public abstract class MemberFormatter {
3136 public virtual string GetName (IMemberReference member)
3138 TypeReference type = member as TypeReference;
3140 return GetTypeName (type);
3141 MethodReference method = member as MethodReference;
3142 if (method != null && method.Name == ".ctor") // method.IsConstructor
3143 return GetConstructorName (method);
3145 return GetMethodName (method);
3146 PropertyReference prop = member as PropertyReference;
3148 return GetPropertyName (prop);
3149 FieldReference field = member as FieldReference;
3151 return GetFieldName (field);
3152 EventReference e = member as EventReference;
3154 return GetEventName (e);
3155 throw new NotSupportedException ("Can't handle: " +
3156 (member == null ? "null" : member.GetType().ToString()));
3159 protected virtual string GetTypeName (TypeReference type)
3162 throw new ArgumentNullException ("type");
3163 return _AppendTypeName (new StringBuilder (type.Name.Length), type).ToString ();
3166 protected virtual char[] ArrayDelimeters {
3167 get {return new char[]{'[', ']'};}
3170 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type)
3172 if (type is ArrayType) {
3173 TypeSpecification spec = type as TypeSpecification;
3174 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3175 .Append (ArrayDelimeters [0]);
3176 ArrayType array = (ArrayType) type;
3177 int rank = array.Rank;
3179 buf.Append (new string (',', rank-1));
3180 return buf.Append (ArrayDelimeters [1]);
3182 if (type is ReferenceType) {
3183 return AppendRefTypeName (buf, type);
3185 if (type is PointerType) {
3186 return AppendPointerTypeName (buf, type);
3188 AppendNamespace (buf, type);
3189 if (type is GenericParameter) {
3190 return AppendTypeName (buf, type);
3192 GenericInstanceType genInst = type as GenericInstanceType;
3193 if (type.GenericParameters.Count == 0 &&
3194 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3195 return AppendFullTypeName (buf, type);
3197 return AppendGenericType (buf, type);
3200 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3202 string ns = DocUtils.GetNamespace (type);
3203 if (ns != null && ns.Length > 0)
3204 buf.Append (ns).Append ('.');
3208 private StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type)
3210 if (type.DeclaringType != null)
3211 AppendFullTypeName (buf, type.DeclaringType).Append (NestedTypeSeparator);
3212 return AppendTypeName (buf, type);
3215 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3217 return AppendTypeName (buf, type.Name);
3220 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3222 int n = typename.IndexOf ("`");
3224 return buf.Append (typename.Substring (0, n));
3225 return buf.Append (typename);
3228 protected virtual string RefTypeModifier {
3232 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type)
3234 TypeSpecification spec = type as TypeSpecification;
3235 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3236 .Append (RefTypeModifier);
3239 protected virtual string PointerModifier {
3243 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type)
3245 TypeSpecification spec = type as TypeSpecification;
3246 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3247 .Append (PointerModifier);
3250 protected virtual char[] GenericTypeContainer {
3251 get {return new char[]{'<', '>'};}
3254 protected virtual char NestedTypeSeparator {
3258 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3260 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3261 type is GenericInstanceType ? type.GetOriginalType () : type);
3262 List<TypeReference> genArgs = GetGenericArguments (type);
3265 bool insertNested = false;
3266 foreach (var decl in decls) {
3267 TypeReference declDef = DocUtils.GetTypeDefinition (decl) ?? decl;
3269 buf.Append (NestedTypeSeparator);
3271 insertNested = true;
3272 AppendTypeName (buf, declDef);
3273 int ac = DocUtils.GetGenericArgumentCount (declDef);
3277 buf.Append (GenericTypeContainer [0]);
3278 _AppendTypeName (buf, genArgs [argIdx++]);
3279 for (int i = 1; i < c; ++i)
3280 _AppendTypeName (buf.Append (","), genArgs [argIdx++]);
3281 buf.Append (GenericTypeContainer [1]);
3287 private List<TypeReference> GetGenericArguments (TypeReference type)
3289 var args = new List<TypeReference> ();
3290 GenericInstanceType inst = type as GenericInstanceType;
3292 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
3294 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
3298 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3303 protected virtual string GetConstructorName (MethodReference constructor)
3305 return constructor.Name;
3308 protected virtual string GetMethodName (MethodReference method)
3313 protected virtual string GetPropertyName (PropertyReference property)
3315 return property.Name;
3318 protected virtual string GetFieldName (FieldReference field)
3323 protected virtual string GetEventName (EventReference e)
3328 public virtual string GetDeclaration (IMemberReference member)
3331 throw new ArgumentNullException ("member");
3332 TypeDefinition type = member as TypeDefinition;
3334 return GetTypeDeclaration (type);
3335 MethodDefinition method = member as MethodDefinition;
3336 if (method != null && method.IsConstructor)
3337 return GetConstructorDeclaration (method);
3339 return GetMethodDeclaration (method);
3340 PropertyDefinition prop = member as PropertyDefinition;
3342 return GetPropertyDeclaration (prop);
3343 FieldDefinition field = member as FieldDefinition;
3345 return GetFieldDeclaration (field);
3346 EventDefinition e = member as EventDefinition;
3348 return GetEventDeclaration (e);
3349 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3352 protected virtual string GetTypeDeclaration (TypeDefinition type)
3355 throw new ArgumentNullException ("type");
3356 StringBuilder buf = new StringBuilder (type.Name.Length);
3357 _AppendTypeName (buf, type);
3358 AppendGenericTypeConstraints (buf, type);
3359 return buf.ToString ();
3362 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
3364 return GetConstructorName (constructor);
3367 protected virtual string GetMethodDeclaration (MethodDefinition method)
3369 // Special signature for destructors.
3370 if (method.Name == "Finalize" && method.Parameters.Count == 0)
3371 return GetFinalizerName (method);
3373 StringBuilder buf = new StringBuilder ();
3375 AppendVisibility (buf, method);
3376 if (buf.Length == 0 &&
3377 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3380 AppendModifiers (buf, method);
3382 if (buf.Length != 0)
3384 buf.Append (GetName (method.ReturnType.ReturnType)).Append (" ");
3386 AppendMethodName (buf, method);
3387 AppendGenericMethod (buf, method).Append (" ");
3388 AppendParameters (buf, method, method.Parameters);
3389 AppendGenericMethodConstraints (buf, method);
3390 return buf.ToString ();
3393 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3395 return buf.Append (method.Name);
3398 protected virtual string GetFinalizerName (MethodDefinition method)
3403 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3408 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3413 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3418 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3423 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3428 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
3430 return GetPropertyName (property);
3433 protected virtual string GetFieldDeclaration (FieldDefinition field)
3435 return GetFieldName (field);
3438 protected virtual string GetEventDeclaration (EventDefinition e)
3440 return GetEventName (e);
3444 class CSharpFullMemberFormatter : MemberFormatter {
3446 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3448 string ns = DocUtils.GetNamespace (type);
3449 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
3450 buf.Append (ns).Append ('.');
3454 private string GetCSharpType (string t)
3457 case "System.Byte": return "byte";
3458 case "System.SByte": return "sbyte";
3459 case "System.Int16": return "short";
3460 case "System.Int32": return "int";
3461 case "System.Int64": return "long";
3463 case "System.UInt16": return "ushort";
3464 case "System.UInt32": return "uint";
3465 case "System.UInt64": return "ulong";
3467 case "System.Single": return "float";
3468 case "System.Double": return "double";
3469 case "System.Decimal": return "decimal";
3470 case "System.Boolean": return "bool";
3471 case "System.Char": return "char";
3472 case "System.Void": return "void";
3473 case "System.String": return "string";
3474 case "System.Object": return "object";
3479 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3481 if (type is GenericParameter)
3482 return buf.Append (type.Name);
3483 string t = type.FullName;
3484 if (!t.StartsWith ("System.")) {
3485 return base.AppendTypeName (buf, type);
3488 string s = GetCSharpType (t);
3490 return buf.Append (s);
3492 return base.AppendTypeName (buf, type);
3495 protected override string GetTypeDeclaration (TypeDefinition type)
3497 string visibility = GetTypeVisibility (type.Attributes);
3498 if (visibility == null)
3501 StringBuilder buf = new StringBuilder ();
3503 buf.Append (visibility);
3506 MemberFormatter full = new CSharpFullMemberFormatter ();
3508 if (DocUtils.IsDelegate (type)) {
3509 buf.Append("delegate ");
3510 MethodDefinition invoke = type.GetMethod ("Invoke");
3511 buf.Append (full.GetName (invoke.ReturnType.ReturnType)).Append (" ");
3512 buf.Append (GetName (type));
3513 AppendParameters (buf, invoke, invoke.Parameters);
3514 AppendGenericTypeConstraints (buf, type);
3517 return buf.ToString();
3520 if (type.IsAbstract && !type.IsInterface)
3521 buf.Append("abstract ");
3522 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
3523 buf.Append("sealed ");
3524 buf.Replace ("abstract sealed", "static");
3526 buf.Append (GetTypeKind (type));
3528 buf.Append (GetCSharpType (type.FullName) == null
3533 TypeReference basetype = type.BaseType;
3534 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
3537 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
3538 .Select (iface => full.GetName (iface))
3542 if (basetype != null || interface_names.Count > 0)
3545 if (basetype != null) {
3546 buf.Append (full.GetName (basetype));
3547 if (interface_names.Count > 0)
3551 for (int i = 0; i < interface_names.Count; i++){
3554 buf.Append (interface_names [i]);
3556 AppendGenericTypeConstraints (buf, type);
3559 return buf.ToString ();
3562 static string GetTypeKind (TypeDefinition t)
3568 if (t.IsClass || t.FullName == "System.Enum")
3572 throw new ArgumentException(t.FullName);
3575 static string GetTypeVisibility (TypeAttributes ta)
3577 switch (ta & TypeAttributes.VisibilityMask) {
3578 case TypeAttributes.Public:
3579 case TypeAttributes.NestedPublic:
3582 case TypeAttributes.NestedFamily:
3583 case TypeAttributes.NestedFamORAssem:
3591 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3593 if (type.GenericParameters.Count == 0)
3595 return AppendConstraints (buf, type.GenericParameters);
3598 private StringBuilder AppendConstraints (StringBuilder buf, GenericParameterCollection genArgs)
3600 foreach (GenericParameter genArg in genArgs) {
3601 GenericParameterAttributes attrs = genArg.Attributes;
3602 ConstraintCollection constraints = genArg.Constraints;
3603 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
3605 buf.Append (" where ").Append (genArg.Name).Append (" : ");
3606 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
3607 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
3608 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
3611 buf.Append ("class");
3615 buf.Append ("struct");
3618 if (constraints.Count > 0 && !isvt) {
3621 buf.Append (GetTypeName (constraints [0]));
3622 for (int i = 1; i < constraints.Count; ++i)
3623 buf.Append (", ").Append (GetTypeName (constraints [i]));
3625 if (isnew && !isvt) {
3628 buf.Append ("new()");
3634 protected override string GetConstructorDeclaration (MethodDefinition constructor)
3636 StringBuilder buf = new StringBuilder ();
3637 AppendVisibility (buf, constructor);
3638 if (buf.Length == 0)
3642 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
3643 AppendParameters (buf, constructor, constructor.Parameters);
3646 return buf.ToString ();
3649 protected override string GetMethodDeclaration (MethodDefinition method)
3651 string decl = base.GetMethodDeclaration (method);
3657 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3659 if (DocUtils.IsExplicitlyImplemented (method)) {
3660 TypeReference iface;
3661 MethodReference ifaceMethod;
3662 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3663 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3665 .Append (ifaceMethod.Name);
3667 return base.AppendMethodName (buf, method);
3670 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3672 if (method.GenericParameters.Count == 0)
3674 return AppendConstraints (buf, method.GenericParameters);
3677 protected override string RefTypeModifier {
3681 protected override string GetFinalizerName (MethodDefinition method)
3683 return "~" + method.DeclaringType.Name + " ()";
3686 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3690 if (method.IsPublic)
3691 return buf.Append ("public");
3692 if (method.IsFamily || method.IsFamilyOrAssembly)
3693 return buf.Append ("protected");
3697 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3699 string modifiers = String.Empty;
3700 if (method.IsStatic) modifiers += " static";
3701 if (method.IsVirtual && !method.IsAbstract) {
3702 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3703 else modifiers += " override";
3705 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
3706 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
3707 if (method.IsFinal) modifiers += " sealed";
3708 if (modifiers == " virtual sealed") modifiers = "";
3710 return buf.Append (modifiers);
3713 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3715 if (method.IsGenericMethod ()) {
3716 GenericParameterCollection args = method.GenericParameters;
3717 if (args.Count > 0) {
3719 buf.Append (args [0].Name);
3720 for (int i = 1; i < args.Count; ++i)
3721 buf.Append (",").Append (args [i].Name);
3728 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3730 return AppendParameters (buf, method, parameters, '(', ')');
3733 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters, char begin, char end)
3737 if (parameters.Count > 0) {
3738 if (DocUtils.IsExtensionMethod (method))
3739 buf.Append ("this ");
3740 AppendParameter (buf, parameters [0]);
3741 for (int i = 1; i < parameters.Count; ++i) {
3743 AppendParameter (buf, parameters [i]);
3747 return buf.Append (end);
3750 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
3752 if (parameter.ParameterType is ReferenceType) {
3753 if (parameter.IsOut)
3754 buf.Append ("out ");
3756 buf.Append ("ref ");
3758 buf.Append (GetName (parameter.ParameterType)).Append (" ");
3759 return buf.Append (parameter.Name);
3762 protected override string GetPropertyDeclaration (PropertyDefinition property)
3764 MethodDefinition method;
3766 string get_visible = null;
3767 if ((method = property.GetMethod) != null &&
3768 (DocUtils.IsExplicitlyImplemented (method) ||
3769 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3770 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3771 string set_visible = null;
3772 if ((method = property.SetMethod) != null &&
3773 (DocUtils.IsExplicitlyImplemented (method) ||
3774 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3775 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3777 if ((set_visible == null) && (get_visible == null))
3781 StringBuilder buf = new StringBuilder ();
3782 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
3783 buf.Append (visibility = get_visible);
3784 else if (set_visible != null && get_visible == null)
3785 buf.Append (visibility = set_visible);
3787 buf.Append (visibility = "public");
3789 // Pick an accessor to use for static/virtual/override/etc. checks.
3790 method = property.SetMethod;
3792 method = property.GetMethod;
3794 string modifiers = String.Empty;
3795 if (method.IsStatic) modifiers += " static";
3796 if (method.IsVirtual && !method.IsAbstract) {
3797 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
3798 modifiers += " virtual";
3800 modifiers += " override";
3802 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
3803 if (method.IsAbstract && !declDef.IsInterface)
3804 modifiers += " abstract";
3806 modifiers += " sealed";
3807 if (modifiers == " virtual sealed")
3809 buf.Append (modifiers).Append (' ');
3811 buf.Append (GetName (property.PropertyType)).Append (' ');
3813 IEnumerable<IMemberReference> defs = property.DeclaringType.GetDefaultMembers ();
3814 string name = property.Name;
3815 foreach (IMemberReference mi in defs) {
3816 if (mi == property) {
3821 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
3823 if (property.Parameters.Count != 0) {
3824 AppendParameters (buf, method, property.Parameters, '[', ']');
3828 if (set_visible != null) {
3829 if (set_visible != visibility)
3830 buf.Append (' ').Append (set_visible);
3831 buf.Append (" set;");
3833 if (get_visible != null) {
3834 if (get_visible != visibility)
3835 buf.Append (' ').Append (get_visible);
3836 buf.Append (" get;");
3840 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
3843 protected override string GetFieldDeclaration (FieldDefinition field)
3845 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
3846 if (declType.IsEnum && field.Name == "value__")
3847 return null; // This member of enums aren't documented.
3849 StringBuilder buf = new StringBuilder ();
3850 AppendFieldVisibility (buf, field);
3851 if (buf.Length == 0)
3854 if (declType.IsEnum)
3857 if (field.IsStatic && !field.IsLiteral)
3858 buf.Append (" static");
3859 if (field.IsInitOnly)
3860 buf.Append (" readonly");
3861 if (field.IsLiteral)
3862 buf.Append (" const");
3864 buf.Append (' ').Append (GetName (field.FieldType)).Append (' ');
3865 buf.Append (field.Name);
3866 AppendFieldValue (buf, field);
3869 return buf.ToString ();
3872 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
3875 return buf.Append ("public");
3876 if (field.IsFamily || field.IsFamilyOrAssembly)
3877 return buf.Append ("protected");
3881 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
3883 // enums have a value__ field, which we ignore
3884 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
3885 field.DeclaringType.IsGenericType ())
3887 if (field.HasConstant && field.IsLiteral) {
3890 val = field.Constant;
3895 buf.Append (" = ").Append ("null");
3896 else if (val is Enum)
3897 buf.Append (" = ").Append (val.ToString ());
3898 else if (val is IFormattable) {
3899 string value = ((IFormattable)val).ToString();
3901 value = "\"" + value + "\"";
3902 buf.Append (" = ").Append (value);
3908 protected override string GetEventDeclaration (EventDefinition e)
3910 StringBuilder buf = new StringBuilder ();
3911 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
3915 AppendModifiers (buf, e.AddMethod);
3917 buf.Append (" event ");
3918 buf.Append (GetName (e.EventType)).Append (' ');
3919 buf.Append (e.Name).Append (';');
3921 return buf.ToString ();
3925 class CSharpMemberFormatter : CSharpFullMemberFormatter {
3926 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3932 class DocTypeFullMemberFormatter : MemberFormatter {
3933 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
3935 protected override char NestedTypeSeparator {
3940 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
3941 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3947 class SlashDocMemberFormatter : MemberFormatter {
3949 protected override char[] GenericTypeContainer {
3950 get {return new char[]{'{', '}'};}
3953 private bool AddTypeCount = true;
3955 private TypeReference genDeclType;
3956 private MethodReference genDeclMethod;
3958 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3960 if (type is GenericParameter) {
3962 if (genDeclType != null) {
3963 GenericParameterCollection genArgs = genDeclType.GenericParameters;
3964 for (int i = 0; i < genArgs.Count; ++i) {
3965 if (genArgs [i].Name == type.Name) {
3966 buf.Append ('`').Append (i);
3971 if (genDeclMethod != null) {
3972 GenericParameterCollection genArgs = null;
3973 if (genDeclMethod.IsGenericMethod ()) {
3974 genArgs = genDeclMethod.GenericParameters;
3975 for (int i = 0; i < genArgs.Count; ++i) {
3976 if (genArgs [i].Name == type.Name) {
3977 buf.Append ("``").Append (i);
3983 if (genDeclType == null && genDeclMethod == null) {
3984 // Probably from within an explicitly implemented interface member,
3985 // where CSC uses parameter names instead of indices (why?), e.g.
3986 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
3987 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
3988 buf.Append (type.Name);
3990 if (buf.Length == l) {
3991 throw new Exception (string.Format (
3992 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
3993 type.Name, genDeclType, genDeclMethod));
3997 base.AppendTypeName (buf, type);
3999 int numArgs = type.GenericParameters.Count;
4000 if (type.DeclaringType != null)
4001 numArgs -= type.GenericParameters.Count;
4003 buf.Append ('`').Append (numArgs);
4010 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
4013 base.AppendGenericType (buf, type);
4015 AppendType (buf, type);
4019 private StringBuilder AppendType (StringBuilder buf, TypeReference type)
4021 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
4022 bool insertNested = false;
4023 int prevParamCount = 0;
4024 foreach (var decl in decls) {
4026 buf.Append (NestedTypeSeparator);
4027 insertNested = true;
4028 base.AppendTypeName (buf, decl);
4029 int argCount = DocUtils.GetGenericArgumentCount (decl);
4030 int numArgs = argCount - prevParamCount;
4031 prevParamCount = argCount;
4033 buf.Append ('`').Append (numArgs);
4038 public override string GetDeclaration (IMemberReference member)
4040 TypeReference r = member as TypeReference;
4042 return "T:" + GetTypeName (r);
4044 return base.GetDeclaration (member);
4047 protected override string GetConstructorName (MethodReference constructor)
4049 return GetMethodDefinitionName (constructor, "#ctor");
4052 protected override string GetMethodName (MethodReference method)
4055 MethodDefinition methodDef = method as MethodDefinition;
4056 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
4059 TypeReference iface;
4060 MethodReference ifaceMethod;
4061 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
4062 AddTypeCount = false;
4063 name = GetTypeName (iface) + "." + ifaceMethod.Name;
4064 AddTypeCount = true;
4066 return GetMethodDefinitionName (method, name);
4069 private string GetMethodDefinitionName (MethodReference method, string name)
4071 StringBuilder buf = new StringBuilder ();
4072 buf.Append (GetTypeName (method.DeclaringType));
4074 buf.Append (name.Replace (".", "#"));
4075 if (method.IsGenericMethod ()) {
4076 GenericParameterCollection genArgs = method.GenericParameters;
4077 if (genArgs.Count > 0)
4078 buf.Append ("``").Append (genArgs.Count);
4080 ParameterDefinitionCollection parameters = method.Parameters;
4082 genDeclType = method.DeclaringType;
4083 genDeclMethod = method;
4084 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
4088 genDeclMethod = null;
4090 return buf.ToString ();
4093 private StringBuilder AppendParameters (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinitionCollection parameters)
4095 if (parameters.Count == 0)
4100 AppendParameter (buf, genArgs, parameters [0]);
4101 for (int i = 1; i < parameters.Count; ++i) {
4103 AppendParameter (buf, genArgs, parameters [i]);
4106 return buf.Append (')');
4109 private StringBuilder AppendParameter (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinition parameter)
4111 AddTypeCount = false;
4112 buf.Append (GetTypeName (parameter.ParameterType));
4113 AddTypeCount = true;
4117 protected override string GetPropertyName (PropertyReference property)
4121 PropertyDefinition propertyDef = property as PropertyDefinition;
4122 MethodDefinition method = null;
4123 if (propertyDef != null)
4124 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
4125 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
4126 name = property.Name;
4128 TypeReference iface;
4129 MethodReference ifaceMethod;
4130 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4131 AddTypeCount = false;
4132 name = string.Join ("#", new string[]{
4133 GetTypeName (iface).Replace (".", "#"),
4134 DocUtils.GetMember (property.Name)
4136 AddTypeCount = true;
4139 StringBuilder buf = new StringBuilder ();
4140 buf.Append (GetName (property.DeclaringType));
4143 ParameterDefinitionCollection parameters = property.Parameters;
4144 if (parameters.Count > 0) {
4145 genDeclType = property.DeclaringType;
4147 GenericParameterCollection genArgs = property.DeclaringType.GenericParameters;
4148 AppendParameter (buf, genArgs, parameters [0]);
4149 for (int i = 1; i < parameters.Count; ++i) {
4151 AppendParameter (buf, genArgs, parameters [i]);
4156 return buf.ToString ();
4159 protected override string GetFieldName (FieldReference field)
4161 return string.Format ("{0}.{1}",
4162 GetName (field.DeclaringType), field.Name);
4165 protected override string GetEventName (EventReference e)
4167 return string.Format ("{0}.{1}",
4168 GetName (e.DeclaringType), e.Name);
4171 protected override string GetTypeDeclaration (TypeDefinition type)
4173 string name = GetName (type);
4179 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4181 string name = GetName (constructor);
4187 protected override string GetMethodDeclaration (MethodDefinition method)
4189 string name = GetName (method);
4192 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4193 genDeclType = method.DeclaringType;
4194 genDeclMethod = method;
4195 name += "~" + GetName (method.ReturnType.ReturnType);
4197 genDeclMethod = null;
4202 protected override string GetPropertyDeclaration (PropertyDefinition property)
4204 string name = GetName (property);
4210 protected override string GetFieldDeclaration (FieldDefinition field)
4212 string name = GetName (field);
4218 protected override string GetEventDeclaration (EventDefinition e)
4220 string name = GetName (e);
4227 class FileNameMemberFormatter : SlashDocMemberFormatter {
4228 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4233 protected override char NestedTypeSeparator {