1 // Updater program for syncing Mono's ECMA-style documentation files
3 // By Joshua Tauberer <tauberer@for.net>
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Diagnostics;
9 using System.Globalization;
14 using System.Xml.XPath;
19 using MyXmlNodeList = System.Collections.Generic.List<System.Xml.XmlNode>;
20 using StringList = System.Collections.Generic.List<string>;
21 using StringToStringMap = System.Collections.Generic.Dictionary<string, string>;
22 using StringToXmlNodeMap = System.Collections.Generic.Dictionary<string, System.Xml.XmlNode>;
24 namespace Mono.Documentation {
26 class MDocUpdater : MDocCommand
29 List<AssemblyDefinition> assemblies;
33 bool no_assembly_versions;
34 ExceptionLocations? exceptions;
36 int additions = 0, deletions = 0;
38 static XmlDocument slashdocs;
43 static readonly MemberFormatter csharpFullFormatter = new CSharpFullMemberFormatter ();
44 static readonly MemberFormatter csharpFormatter = new CSharpMemberFormatter ();
45 static readonly MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
46 static readonly MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
47 static readonly MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
49 MyXmlNodeList extensionMethods = new MyXmlNodeList ();
51 public override void Run (IEnumerable<string> args)
53 show_exceptions = DebugOutput;
55 var types = new List<string> ();
56 var p = new OptionSet () {
58 "Delete removed members from the XML files.",
59 v => delete = v != null },
61 "Document potential exceptions that members can generate. {SOURCES} " +
62 "is a comma-separated list of:\n" +
63 " asm Method calls in same assembly\n" +
64 " depasm Method calls in dependent assemblies\n" +
65 " all Record all possible exceptions\n" +
66 "If nothing is specified, then only exceptions from the member will " +
68 v => exceptions = ParseExceptionLocations (v) },
70 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
73 case "no-assembly-versions":
74 no_assembly_versions = true;
77 throw new Exception ("Unsupported flag `" + v + "'.");
80 { "fno-assembly-versions",
81 "Do not generate //AssemblyVersion elements.",
82 v => no_assembly_versions = v != null },
84 "Import documentation from {FILE}.",
87 "Root {DIRECTORY} to generate/update documentation.",
90 "Manually specify the assembly {VERSION} that new members were added in.",
93 "Only update documentation for {TYPE}.",
96 var assemblies = Parse (p, args, "update",
97 "[OPTIONS]+ ASSEMBLIES",
98 "Create or update documentation from ASSEMBLIES.");
99 if (assemblies == null)
101 if (assemblies.Count == 0)
102 Error ("No assemblies specified.");
104 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
107 throw new InvalidOperationException("The --out option is required.");
109 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
111 if (import != null && ecmadocs == null && slashdocs == null) {
113 XmlReader r = new XmlTextReader (import);
115 while (r.NodeType != XmlNodeType.Element) {
117 Error ("Unable to read XML file: {0}.", import);
119 if (r.LocalName == "doc") {
120 slashdocs = new XmlDocument();
121 slashdocs.Load (import);
123 else if (r.LocalName == "Libraries") {
124 ecmadocs = new XmlTextReader (import);
127 Error ("Unsupported XML format within {0}.", import);
130 } catch (Exception e) {
131 Environment.ExitCode = 1;
132 Error ("Could not load XML file: {0}.", e.Message);
136 // PERFORM THE UPDATES
139 DoUpdateTypes (srcPath, types, srcPath);
141 else if (opts.@namespace != null)
142 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
143 Path.Combine (dest_dir, opts.@namespace));
146 DoUpdateAssemblies (srcPath, srcPath);
148 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
151 static ExceptionLocations ParseExceptionLocations (string s)
153 ExceptionLocations loc = ExceptionLocations.Member;
156 foreach (var type in s.Split (',')) {
158 case "asm": loc |= ExceptionLocations.Assembly; break;
159 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
160 case "all": loc = ExceptionLocations.All; break;
161 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
167 private void Warning (string format, params object[] args)
169 Message (TraceLevel.Warning, "mdoc: " + format, args);
172 private static AssemblyDefinition LoadAssembly (string name)
174 AssemblyDefinition assembly = null;
176 assembly = AssemblyFactory.GetAssembly (name);
177 } catch (System.IO.FileNotFoundException) { }
179 if (assembly == null)
180 throw new InvalidOperationException("Assembly " + name + " not found.");
182 var r = assembly.Resolver as BaseAssemblyResolver;
183 if (r != null && name.Contains (Path.DirectorySeparatorChar)) {
184 r.AddSearchDirectory (Path.GetDirectoryName (name));
189 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
190 OrderTypeAttributes (element);
191 XmlTextWriter writer = new XmlTextWriter(output);
192 writer.Formatting = Formatting.Indented;
193 writer.Indentation = 2;
194 writer.IndentChar = ' ';
195 element.WriteTo(writer);
199 private static void OrderTypeAttributes (XmlElement e)
201 foreach (XmlElement type in e.SelectNodes ("//Type")) {
202 OrderTypeAttributes (type.Attributes);
206 static readonly string[] TypeAttributeOrder = {
207 "Name", "FullName", "FullNameSP", "Maintainer"
210 private static void OrderTypeAttributes (XmlAttributeCollection c)
212 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
213 for (int i = 0; i < c.Count; ++i) {
214 XmlAttribute a = c [i];
215 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
216 if (a.Name == TypeAttributeOrder [j]) {
222 for (int i = attrs.Length-1; i >= 0; --i) {
223 XmlAttribute n = attrs [i];
226 XmlAttribute r = null;
227 for (int j = i+1; j < attrs.Length; ++j) {
228 if (attrs [j] != null) {
236 c.InsertBefore (n, r);
240 private XmlDocument CreateIndexStub()
242 XmlDocument index = new XmlDocument();
244 XmlElement index_root = index.CreateElement("Overview");
245 index.AppendChild(index_root);
247 if (assemblies.Count == 0)
248 throw new Exception ("No assembly");
250 XmlElement index_assemblies = index.CreateElement("Assemblies");
251 index_root.AppendChild(index_assemblies);
253 XmlElement index_remarks = index.CreateElement("Remarks");
254 index_remarks.InnerText = "To be added.";
255 index_root.AppendChild(index_remarks);
257 XmlElement index_copyright = index.CreateElement("Copyright");
258 index_copyright.InnerText = "To be added.";
259 index_root.AppendChild(index_copyright);
261 XmlElement index_types = index.CreateElement("Types");
262 index_root.AppendChild(index_types);
267 private static void WriteNamespaceStub(string ns, string outdir) {
268 XmlDocument index = new XmlDocument();
270 XmlElement index_root = index.CreateElement("Namespace");
271 index.AppendChild(index_root);
273 index_root.SetAttribute("Name", ns);
275 XmlElement index_docs = index.CreateElement("Docs");
276 index_root.AppendChild(index_docs);
278 XmlElement index_summary = index.CreateElement("summary");
279 index_summary.InnerText = "To be added.";
280 index_docs.AppendChild(index_summary);
282 XmlElement index_remarks = index.CreateElement("remarks");
283 index_remarks.InnerText = "To be added.";
284 index_docs.AppendChild(index_remarks);
286 using (TextWriter writer = OpenWrite (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew)) {
287 WriteXml(index.DocumentElement, writer);
291 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
293 var found = new HashSet<string> ();
294 foreach (AssemblyDefinition assembly in assemblies) {
295 foreach (DocsTypeInfo docsTypeInfo in GetTypes (assembly, typenames)) {
296 string relpath = DoUpdateType (docsTypeInfo.Type, basepath, dest, docsTypeInfo.EcmaDocs);
298 found.Add (docsTypeInfo.Type.FullName);
301 var notFound = from n in typenames where !found.Contains (n) select n;
303 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
306 public string DoUpdateType (TypeDefinition type, string basepath, string dest, XmlReader ecmaDocsType)
308 if (type.Namespace == null)
309 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
311 if (!IsPublic (type))
314 // Must get the A+B form of the type name.
315 string typename = GetTypeFileName(type);
317 string reltypefile = DocUtils.PathCombine (DocUtils.GetNamespace (type), typename + ".xml");
318 string typefile = Path.Combine (basepath, reltypefile);
319 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
321 string output = null;
324 } else if (dest == "-") {
327 output = Path.Combine (dest, reltypefile);
332 XmlDocument basefile = new XmlDocument();
334 basefile.Load(typefile);
335 } catch (Exception e) {
336 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
339 DoUpdateType2("Updating", basefile, type, output, false, ecmaDocsType);
342 XmlElement td = StubType(type, output, ecmaDocsType);
346 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
349 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
355 public void DoUpdateNS (string ns, string nspath, string outpath)
357 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
358 AssemblyDefinition assembly = assemblies [0];
360 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
361 XmlDocument basefile = new XmlDocument();
362 string typefile = Path.Combine(nspath, file.Name);
364 basefile.Load(typefile);
365 } catch (Exception e) {
366 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
370 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
371 TypeDefinition type = assembly.GetType(typename);
373 Warning ("Type no longer in assembly: " + typename);
377 seenTypes[type] = seenTypes;
378 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false, null);
381 // Stub types not in the directory
382 foreach (DocsTypeInfo docsTypeInfo in GetTypes (assembly, null)) {
383 TypeDefinition type = docsTypeInfo.Type;
384 if (type.Namespace != ns || seenTypes.ContainsKey(type))
387 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"), docsTypeInfo.EcmaDocs);
388 if (td == null) continue;
392 private static string GetTypeFileName (TypeReference type)
394 return filenameFormatter.GetName (type);
397 public static string GetTypeFileName (string typename)
399 StringBuilder filename = new StringBuilder (typename.Length);
403 for (int i = 0; i < typename.Length; ++i) {
404 char c = typename [i];
413 filename.Append ('`').Append ((numArgs+1).ToString());
428 return filename.ToString ();
431 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
433 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
434 index_assembly.SetAttribute ("Name", assembly.Name.Name);
435 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
436 MakeAttributes (index_assembly, assembly.CustomAttributes, true);
437 parent.AppendChild(index_assembly);
440 private void DoUpdateAssemblies (string source, string dest)
442 string indexfile = dest + "/index.xml";
444 if (System.IO.File.Exists(indexfile)) {
445 index = new XmlDocument();
446 index.Load(indexfile);
449 ClearElement(index.DocumentElement, "Assembly");
450 ClearElement(index.DocumentElement, "Attributes");
452 index = CreateIndexStub();
455 string defaultTitle = "Untitled";
456 if (assemblies.Count == 1)
457 defaultTitle = assemblies[0].Name.Name;
458 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
460 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
461 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
462 index_assemblies.RemoveAll ();
465 HashSet<string> goodfiles = new HashSet<string> ();
467 foreach (AssemblyDefinition assm in assemblies) {
468 AddIndexAssembly (assm, index_assemblies);
469 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
472 SortIndexEntries (index_types);
474 CleanupFiles (dest, goodfiles);
475 CleanupIndexTypes (index_types, goodfiles);
476 CleanupExtensions (index_types);
478 using (TextWriter writer = OpenWrite (indexfile, FileMode.Create))
479 WriteXml(index.DocumentElement, writer);
482 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
484 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
486 foreach (DocsTypeInfo docTypeInfo in GetTypes (assembly, null)) {
487 TypeDefinition type = docTypeInfo.Type;
488 string typename = GetTypeFileName(type);
489 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0)
492 string reltypepath = DoUpdateType (type, source, dest, docTypeInfo.EcmaDocs);
493 if (reltypepath == null)
496 // Add namespace and type nodes into the index file as needed
497 string ns = DocUtils.GetNamespace (type);
498 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode("Namespace[@Name='" + ns + "']");
499 if (nsnode == null) {
500 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
501 nsnode.SetAttribute ("Name", ns);
502 index_types.AppendChild(nsnode);
504 string doc_typename = GetDocTypeName (type);
505 XmlElement typenode = (XmlElement)nsnode.SelectSingleNode("Type[@Name='" + typename + "']");
506 if (typenode == null) {
507 typenode = index_types.OwnerDocument.CreateElement("Type");
508 typenode.SetAttribute("Name", typename);
509 nsnode.AppendChild(typenode);
511 if (typename != doc_typename)
512 typenode.SetAttribute("DisplayName", doc_typename);
514 typenode.RemoveAttribute("DisplayName");
515 typenode.SetAttribute ("Kind", GetTypeKind (type));
517 // Ensure the namespace index file exists
518 string onsdoc = DocUtils.PathCombine (dest, type.Namespace + ".xml");
519 string nsdoc = DocUtils.PathCombine (dest, "ns-" + type.Namespace + ".xml");
520 if (File.Exists (onsdoc)) {
521 File.Move (onsdoc, nsdoc);
524 if (!File.Exists (nsdoc)) {
525 Console.WriteLine("New Namespace File: " + type.Namespace);
526 WriteNamespaceStub(type.Namespace, dest);
529 goodfiles.Add (reltypepath);
534 public TypeDefinition Type;
535 public XmlReader EcmaDocs;
537 public DocsTypeInfo (TypeDefinition type, XmlReader docs)
540 this.EcmaDocs = docs;
544 IEnumerable<Mono.Documentation.MDocUpdater.DocsTypeInfo> GetTypes (AssemblyDefinition assembly, List<string> forTypes)
546 HashSet<string> seen = null;
547 if (forTypes != null)
549 if (ecmadocs != null) {
550 seen = new HashSet<string> ();
552 while (ecmadocs.Read ()) {
553 switch (ecmadocs.Name) {
556 typeDepth = ecmadocs.Depth;
557 if (ecmadocs.NodeType != XmlNodeType.Element)
559 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
561 string typename = ecmadocs.GetAttribute ("FullName");
562 string typename2 = GetTypeFileName (typename);
563 if (forTypes != null &&
564 forTypes.BinarySearch (typename) < 0 &&
565 typename != typename2 &&
566 forTypes.BinarySearch (typename2) < 0)
569 if ((t = assembly.GetType (typename)) == null &&
570 (t = assembly.GetType (typename2)) == null)
573 if (typename != typename2)
574 seen.Add (typename2);
575 Console.WriteLine (" Import: {0}", t.FullName);
576 yield return new DocsTypeInfo (t, ecmadocs);
584 foreach (TypeDefinition type in assembly.GetTypes()) {
585 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
587 if (seen != null && seen.Contains (type.FullName))
589 yield return new DocsTypeInfo (type, null);
590 foreach (TypeDefinition nested in type.NestedTypes)
591 yield return new DocsTypeInfo (nested, null);
595 private static void SortIndexEntries (XmlElement indexTypes)
597 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
598 XmlNodeComparer c = new AttributeNameComparer ();
599 SortXmlNodes (indexTypes, namespaces, c);
601 for (int i = 0; i < namespaces.Count; ++i)
602 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
605 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
607 MyXmlNodeList l = new MyXmlNodeList (children.Count);
608 for (int i = 0; i < children.Count; ++i)
609 l.Add (children [i]);
611 for (int i = l.Count - 1; i > 0; --i) {
612 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
616 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
618 public abstract int Compare (XmlNode x, XmlNode y);
620 public int Compare (object x, object y)
622 return Compare ((XmlNode) x, (XmlNode) y);
626 class AttributeNameComparer : XmlNodeComparer {
629 public AttributeNameComparer ()
634 public AttributeNameComparer (string attribute)
636 this.attribute = attribute;
639 public override int Compare (XmlNode x, XmlNode y)
641 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
645 class VersionComparer : XmlNodeComparer {
646 public override int Compare (XmlNode x, XmlNode y)
648 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
649 string a = GetVersion (x.InnerText);
650 string b = GetVersion (y.InnerText);
651 return new Version (a).CompareTo (new Version (b));
654 static string GetVersion (string v)
656 int n = v.IndexOf ("x");
659 return v.Substring (0, n-1);
663 private static string GetTypeKind (TypeDefinition type)
666 return "Enumeration";
667 if (type.IsValueType)
669 if (type.IsInterface)
671 if (DocUtils.IsDelegate (type))
673 if (type.IsClass || type.FullName == "System.Enum") // FIXME
675 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
678 private static bool IsPublic (TypeDefinition type)
680 TypeDefinition decl = type;
681 while (decl != null) {
682 if (!(decl.IsPublic || decl.IsNestedPublic)) {
685 decl = (TypeDefinition) decl.DeclaringType;
690 private void CleanupFiles (string dest, HashSet<string> goodfiles)
692 // Look for files that no longer correspond to types
693 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
694 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
695 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
696 if (!goodfiles.Contains (relTypeFile)) {
697 XmlDocument doc = new XmlDocument ();
698 doc.Load (typefile.FullName);
699 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
700 if (UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
701 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
702 WriteXml(doc.DocumentElement, writer);
703 goodfiles.Add (relTypeFile);
706 string newname = typefile.FullName + ".remove";
707 try { System.IO.File.Delete(newname); } catch (Exception) { }
708 try { typefile.MoveTo(newname); } catch (Exception) { }
709 Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
715 private static TextWriter OpenWrite (string path, FileMode mode)
717 return new StreamWriter (
718 new FileStream (path, mode),
719 new UTF8Encoding (false)
723 private string[] GetAssemblyVersions ()
725 return (from a in assemblies select GetAssemblyVersion (a)).ToArray ();
728 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
730 // Look for type nodes that no longer correspond to types
731 MyXmlNodeList remove = new MyXmlNodeList ();
732 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
733 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
734 if (!goodfiles.Contains (fulltypename)) {
735 remove.Add (typenode);
738 foreach (XmlNode n in remove)
739 n.ParentNode.RemoveChild (n);
742 private void CleanupExtensions (XmlElement index_types)
744 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
745 if (extensionMethods.Count == 0) {
748 index_types.RemoveChild (e);
752 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
753 index_types.SelectSingleNode ("/Overview").AppendChild (e);
757 extensionMethods.Sort (DefaultExtensionMethodComparer);
758 foreach (XmlNode m in extensionMethods) {
759 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
763 class ExtensionMethodComparer : XmlNodeComparer {
764 public override int Compare (XmlNode x, XmlNode y)
766 XmlNode xLink = x.SelectSingleNode ("Member/Link");
767 XmlNode yLink = y.SelectSingleNode ("Member/Link");
769 int n = xLink.Attributes ["Type"].Value.CompareTo (
770 yLink.Attributes ["Type"].Value);
773 n = xLink.Attributes ["Member"].Value.CompareTo (
774 yLink.Attributes ["Member"].Value);
775 if (n == 0 && !object.ReferenceEquals (x, y))
776 throw new InvalidOperationException ("Duplicate extension method found!");
781 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
783 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince, XmlReader ecmaDocsType)
785 Console.WriteLine(message + ": " + type.FullName);
787 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
789 // Update type metadata
790 UpdateType(basefile.DocumentElement, type, ecmaDocsType);
792 if (ecmaDocsType != null) {
793 while (ecmaDocsType.Name != "Members" && ecmaDocsType.Read ()) {
796 if (ecmaDocsType.IsEmptyElement)
800 // Update existing members. Delete member nodes that no longer should be there,
801 // and remember what members are already documented so we don't add them again.
803 MyXmlNodeList todelete = new MyXmlNodeList ();
804 foreach (DocsNodeInfo info in GetDocumentationMembers (basefile, type, ecmaDocsType)) {
805 XmlElement oldmember = info.Node;
806 IMemberReference oldmember2 = info.Member;
807 string sig = oldmember2 != null ? MakeMemberSignature(oldmember2) : null;
809 // Interface implementations and overrides are deleted from the docs
810 // unless the overrides option is given.
811 if (oldmember2 != null && sig == null)
814 // Deleted (or signature changed)
815 if (oldmember2 == null) {
816 if (UpdateAssemblyVersions (oldmember, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
818 DeleteMember ("Member Removed", output, oldmember, todelete);
823 if (seenmembers.ContainsKey (sig)) {
824 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
825 // ignore, already seen
827 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
828 DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
830 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
834 // Update signature information
837 seenmembers.Add (sig, oldmember);
839 foreach (XmlElement oldmember in todelete)
840 oldmember.ParentNode.RemoveChild (oldmember);
843 if (!DocUtils.IsDelegate (type)) {
844 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
845 foreach (IMemberReference m in type.GetMembers()) {
846 if (m is TypeDefinition) continue;
848 string sig = MakeMemberSignature(m);
849 if (sig == null) continue;
850 if (seenmembers.ContainsKey(sig)) continue;
852 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
853 if (mm == null) continue;
854 members.AppendChild( mm );
856 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
861 // Import code snippets from files
862 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
863 if (!(code is XmlElement)) continue;
864 string file = ((XmlElement)code).GetAttribute("src");
865 string lang = ((XmlElement)code).GetAttribute("lang");
867 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
869 code.InnerText = src;
873 if (insertSince && since != null) {
874 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
875 docs.AppendChild (CreateSinceNode (basefile));
879 XmlElement d = basefile.DocumentElement ["Docs"];
880 XmlElement m = basefile.DocumentElement ["Members"];
881 if (d != null && m != null)
882 basefile.DocumentElement.InsertBefore (
883 basefile.DocumentElement.RemoveChild (d), m);
887 System.IO.TextWriter writer;
889 writer = Console.Out;
891 FileInfo file = new FileInfo (output);
892 if (!file.Directory.Exists) {
893 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
894 file.Directory.Create ();
896 writer = OpenWrite (output, FileMode.Create);
900 WriteXml(basefile.DocumentElement, writer);
903 private string GetCodeSource (string lang, string file)
906 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
907 // Grab the specified region
908 string region = "#region " + file.Substring (anchorStart + 4);
909 file = file.Substring (0, anchorStart + 3);
911 using (StreamReader reader = new StreamReader (file)) {
913 StringBuilder src = new StringBuilder ();
915 while ((line = reader.ReadLine ()) != null) {
916 if (line.Trim() == region) {
917 indent = line.IndexOf (region);
920 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
925 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
928 return src.ToString ();
930 } catch (Exception e) {
931 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
932 file, region, show_exceptions ? e.ToString () : e.Message);
937 using (StreamReader reader = new StreamReader (file))
938 return reader.ReadToEnd ();
939 } catch (Exception e) {
940 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
945 private IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type, XmlReader ecmaDocsMembers)
947 if (ecmaDocsMembers != null) {
948 int membersDepth = ecmaDocsMembers.Depth;
950 while (go && ecmaDocsMembers.Read ()) {
951 switch (ecmaDocsMembers.Name) {
953 if (membersDepth != ecmaDocsMembers.Depth - 1 || ecmaDocsMembers.NodeType != XmlNodeType.Element)
955 DocumentationMember dm = new DocumentationMember (ecmaDocsMembers);
956 string xp = GetXPathForMember (dm);
957 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
959 if (oldmember == null) {
960 m = GetMember (type, dm);
962 Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
963 type.FullName, dm.MemberSignatures ["C#"]);
964 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
967 // oldmember lookup may have failed due to type parameter renames.
969 oldmember = (XmlElement) basefile.SelectSingleNode (GetXPathForMember (m));
970 if (oldmember == null) {
971 XmlElement members = WriteElement(basefile.DocumentElement, "Members");
972 oldmember = basefile.CreateElement ("Member");
973 oldmember.SetAttribute ("MemberName", dm.MemberName);
974 members.AppendChild (oldmember);
975 foreach (string key in Sort (dm.MemberSignatures.Keys)) {
976 XmlElement ms = basefile.CreateElement ("MemberSignature");
977 ms.SetAttribute ("Language", key);
978 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
979 oldmember.AppendChild (ms);
981 oldmember.SetAttribute ("__monodocer-seen__", "true");
982 Console.WriteLine ("Member Added: {0}", MakeMemberSignature (m));
987 m = GetMember (type, new DocumentationMember (oldmember));
989 Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
990 type.FullName, dm.MemberSignatures ["C#"]);
993 oldmember.SetAttribute ("__monodocer-seen__", "true");
995 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
996 if (ecmaDocsMembers.Name != "Docs")
997 throw new InvalidOperationException ("Found " + ecmaDocsMembers.Name + "; expected <Docs/>!");
998 node.EcmaDocs = ecmaDocsMembers;
1003 if (membersDepth == ecmaDocsMembers.Depth && ecmaDocsMembers.NodeType == XmlNodeType.EndElement) {
1010 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
1011 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
1012 oldmember.RemoveAttribute ("__monodocer-seen__");
1015 IMemberReference m = GetMember (type, new DocumentationMember (oldmember));
1017 yield return new DocsNodeInfo (oldmember);
1020 yield return new DocsNodeInfo (oldmember, m);
1025 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
1027 string format = output != null
1028 ? "{0}: File='{1}'; Signature='{4}'"
1029 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1033 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1034 member.Attributes ["MemberName"].Value,
1035 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
1036 if (!delete && MemberDocsHaveUserContent (member)) {
1037 Warning ("Member deletions must be enabled with the --delete option.");
1039 todelete.Add (member);
1044 class MemberComparer : XmlNodeComparer {
1045 public override int Compare (XmlNode x, XmlNode y)
1048 string xMemberName = x.Attributes ["MemberName"].Value;
1049 string yMemberName = y.Attributes ["MemberName"].Value;
1051 // generic methods *end* with '>'
1052 // it's possible for explicitly implemented generic interfaces to
1053 // contain <...> without being a generic method
1054 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1055 (r = xMemberName.CompareTo (yMemberName)) != 0)
1059 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1060 xMemberName = xMemberName.Substring (0, lt);
1061 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1062 yMemberName = yMemberName.Substring (0, lt);
1063 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1066 // if @MemberName matches, then it's either two different types of
1067 // members sharing the same name, e.g. field & property, or it's an
1068 // overloaded method.
1069 // for different type, sort based on MemberType value.
1070 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1071 y.SelectSingleNode ("MemberType").InnerText);
1075 // same type -- must be an overloaded method. Sort based on type
1076 // parameter count, then parameter count, then by the parameter
1078 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1079 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1080 if (xTypeParams.Count != yTypeParams.Count)
1081 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1082 for (int i = 0; i < xTypeParams.Count; ++i) {
1083 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1084 yTypeParams [i].Attributes ["Name"].Value);
1089 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1090 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1091 if (xParams.Count != yParams.Count)
1092 return xParams.Count <= yParams.Count ? -1 : 1;
1093 for (int i = 0; i < xParams.Count; ++i) {
1094 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1095 yParams [i].Attributes ["Type"].Value);
1099 // all parameters match, but return value might not match if it was
1100 // changed between one version and another.
1101 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1102 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1103 if (xReturn != null && yReturn != null) {
1104 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1113 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1115 private static void SortTypeMembers (XmlNode members)
1117 if (members == null)
1119 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1122 private static bool MemberDocsHaveUserContent (XmlNode e)
1124 e = (XmlElement)e.SelectSingleNode("Docs");
1125 if (e == null) return false;
1126 foreach (XmlElement d in e.SelectNodes("*"))
1127 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1132 // UPDATE HELPER FUNCTIONS
1134 private static IMemberReference GetMember (TypeDefinition type, DocumentationMember member)
1136 string membertype = member.MemberType;
1138 string returntype = member.ReturnType;
1140 string docName = member.MemberName;
1141 string[] docTypeParams = GetTypeParameters (docName);
1143 // Loop through all members in this type with the same name
1144 foreach (IMemberReference mi in GetReflectionMembers (type, docName)) {
1145 if (mi is TypeDefinition) continue;
1146 if (GetMemberType(mi) != membertype) continue;
1148 string sig = MakeMemberSignature(mi);
1149 if (sig == null) continue; // not publicly visible
1151 ParameterDefinitionCollection pis = null;
1152 string[] typeParams = null;
1153 if (mi is MethodDefinition) {
1154 MethodDefinition mb = (MethodDefinition) mi;
1155 pis = mb.Parameters;
1156 if (docTypeParams != null && mb.IsGenericMethod ()) {
1157 GenericParameterCollection args = mb.GenericParameters;
1158 if (args.Count == docTypeParams.Length) {
1159 typeParams = args.Cast<GenericParameter> ().Select (p => p.Name).ToArray ();
1163 else if (mi is PropertyDefinition)
1164 pis = ((PropertyDefinition)mi).Parameters;
1166 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
1167 int pcount = pis == null ? 0 : pis.Count;
1168 if (mcount != pcount)
1171 MethodDefinition mDef = mi as MethodDefinition;
1172 if (mDef != null && !mDef.IsConstructor) {
1173 // Casting operators can overload based on return type.
1174 if (returntype != GetReplacedString (
1175 GetDocTypeFullName (((MethodDefinition)mi).ReturnType.ReturnType),
1176 typeParams, docTypeParams)) {
1184 for (int i = 0; i < pis.Count; i++) {
1185 string paramType = GetReplacedString (
1186 GetDocParameterType (pis [i].ParameterType),
1187 typeParams, docTypeParams);
1188 if (paramType != (string) member.Parameters [i]) {
1193 if (!good) continue;
1201 private static IEnumerable<IMemberReference> GetReflectionMembers (TypeDefinition type, string docName)
1203 // need to worry about 4 forms of //@MemberName values:
1204 // 1. "Normal" (non-generic) member names: GetEnumerator
1206 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
1207 // - try as-is, and try type.member (due to "kludge" for property
1209 // 3. "Normal" Generic member names: Sort<T> (CSC)
1210 // - need to remove generic parameters --> "Sort"
1211 // 4. Explicitly-implemented interface members for generic interfaces:
1212 // -- System.Collections.Generic.IEnumerable<T>.Current
1213 // - Try as-is, and try type.member, *keeping* the generic parameters.
1214 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
1215 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
1216 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
1217 // this as (1) or (2).
1218 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
1220 foreach (IMemberReference mi in type.GetMembers (docName))
1222 if (CountChars (docName, '.') > 0)
1223 // might be a property; try only type.member instead of
1224 // namespace.type.member.
1225 foreach (IMemberReference mi in
1226 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
1233 int startLt, startType, startMethod;
1234 startLt = startType = startMethod = -1;
1235 for (int i = 0; i < docName.Length; ++i) {
1236 switch (docName [i]) {
1245 if (numLt == 0 && (i + 1) < docName.Length)
1246 // there's another character in docName, so this <...> sequence is
1247 // probably part of a generic type -- case 4.
1251 startType = startMethod;
1257 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
1259 foreach (IMemberReference mi in type.GetMembers (refName))
1263 foreach (IMemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
1266 // If we _still_ haven't found it, we've hit another generic naming issue:
1267 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
1268 // explicitly-implemented METHOD names (not properties), e.g.
1269 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
1270 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
1271 // which the XML docs will contain.
1273 // Alas, we can't derive the Mono name from docName, so we need to iterate
1274 // over all member names, convert them into CSC format, and compare... :-(
1277 foreach (IMemberReference mi in type.GetMembers ()) {
1278 if (GetMemberName (mi) == docName)
1283 static string[] GetTypeParameters (string docName)
1285 if (docName [docName.Length-1] != '>')
1287 StringList types = new StringList ();
1288 int endToken = docName.Length-2;
1289 int i = docName.Length-2;
1291 if (docName [i] == ',' || docName [i] == '<') {
1292 types.Add (docName.Substring (i + 1, endToken - i));
1295 if (docName [i] == '<')
1300 return types.ToArray ();
1303 static string GetReplacedString (string typeName, string[] from, string[] to)
1307 for (int i = 0; i < from.Length; ++i)
1308 typeName = typeName.Replace (from [i], to [i]);
1312 // CREATE A STUB DOCUMENTATION FILE
1314 public XmlElement StubType (TypeDefinition type, string output, XmlReader ecmaDocsType)
1316 string typesig = MakeTypeSignature(type);
1317 if (typesig == null) return null; // not publicly visible
1319 XmlDocument doc = new XmlDocument();
1320 XmlElement root = doc.CreateElement("Type");
1321 doc.AppendChild (root);
1323 DoUpdateType2 ("New Type", doc, type, output, true, ecmaDocsType);
1328 private XmlElement CreateSinceNode (XmlDocument doc)
1330 XmlElement s = doc.CreateElement ("since");
1331 s.SetAttribute ("version", since);
1335 // STUBBING/UPDATING FUNCTIONS
1337 public void UpdateType (XmlElement root, TypeDefinition type, XmlReader ecmaDocsType)
1339 root.SetAttribute("Name", GetDocTypeName (type));
1340 root.SetAttribute("FullName", GetDocTypeFullName (type));
1342 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Language", "C#");
1343 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Value", MakeTypeSignature(type));
1345 XmlElement ass = WriteElement(root, "AssemblyInfo");
1346 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1347 if (!no_assembly_versions) {
1348 UpdateAssemblyVersions (root, type, true);
1351 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1352 foreach (var version in versions)
1353 ass.RemoveChild (version);
1355 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1356 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1358 ClearElement(ass, "AssemblyCulture");
1360 // Why-oh-why do we put assembly attributes in each type file?
1361 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1362 // since they're outdated in current docs, and a waste of space.
1363 //MakeAttributes(ass, type.Assembly, true);
1364 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1365 if (assattrs != null)
1366 ass.RemoveChild(assattrs);
1368 NormalizeWhitespace(ass);
1370 if (type.IsGenericType ()) {
1371 MakeTypeParameters (root, type.GenericParameters);
1373 ClearElement(root, "TypeParameters");
1376 if (type.BaseType != null) {
1377 XmlElement basenode = WriteElement(root, "Base");
1379 string basetypename = GetDocTypeFullName (type.BaseType);
1380 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1381 WriteElementText(root, "Base/BaseTypeName", basetypename);
1383 // Document how this type instantiates the generic parameters of its base type
1384 TypeReference origBase = type.BaseType.GetOriginalType ();
1385 if (origBase.IsGenericType ()) {
1386 ClearElement(basenode, "BaseTypeArguments");
1387 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1388 GenericArgumentCollection baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1389 GenericParameterCollection baseGenParams = origBase.GenericParameters;
1390 if (baseGenArgs.Count != baseGenParams.Count)
1391 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1392 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1393 GenericParameter param = baseGenParams [i];
1394 TypeReference value = baseGenArgs [i];
1396 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1397 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1398 bta.AppendChild(arg);
1399 arg.SetAttribute ("TypeParamName", param.Name);
1400 arg.InnerText = GetDocTypeFullName (value);
1404 ClearElement(root, "Base");
1407 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1408 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1409 List<string> interface_names = userInterfaces
1410 .Select (iface => GetDocTypeFullName (iface))
1414 XmlElement interfaces = WriteElement(root, "Interfaces");
1415 interfaces.RemoveAll();
1416 foreach (string iname in interface_names) {
1417 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1418 interfaces.AppendChild(iface);
1419 WriteElementText(iface, "InterfaceName", iname);
1422 ClearElement(root, "Interfaces");
1425 MakeAttributes (root, type.CustomAttributes, false);
1427 if (DocUtils.IsDelegate (type)) {
1428 MakeTypeParameters (root, type.GenericParameters);
1429 MakeParameters(root, type.GetMethod("Invoke").Parameters);
1430 MakeReturnValue(root, type.GetMethod("Invoke"));
1433 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1434 if (ecmaDocsType != null) {
1435 if (ecmaDocsType.Name != "Docs") {
1436 int depth = ecmaDocsType.Depth;
1437 while (ecmaDocsType.Read ()) {
1438 if (ecmaDocsType.Name == "Docs" && ecmaDocsType.Depth == depth + 1)
1442 if (!ecmaDocsType.IsStartElement ("Docs"))
1443 throw new InvalidOperationException ("Found " + ecmaDocsType.Name + "; expecting <Docs/>!");
1444 typeInfo.EcmaDocs = ecmaDocsType;
1446 MakeDocNode (typeInfo);
1448 if (!DocUtils.IsDelegate (type))
1449 WriteElement (root, "Members");
1451 NormalizeWhitespace(root);
1454 static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1456 List<T> l = new List<T> (list);
1461 private void UpdateMember (DocsNodeInfo info)
1463 XmlElement me = (XmlElement) info.Node;
1464 IMemberReference mi = info.Member;
1465 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Language", "C#");
1466 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Value", MakeMemberSignature(mi));
1468 WriteElementText(me, "MemberType", GetMemberType(mi));
1470 if (!no_assembly_versions) {
1471 UpdateAssemblyVersions (me, mi, true);
1474 ClearElement (me, "AssemblyInfo");
1476 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1478 MakeAttributes (me, p.CustomAttributes, false);
1479 MakeReturnValue(me, mi);
1480 if (mi is MethodReference) {
1481 MethodReference mb = (MethodReference) mi;
1482 if (mb.IsGenericMethod ())
1483 MakeTypeParameters (me, mb.GenericParameters);
1485 MakeParameters(me, mi);
1488 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1489 WriteElementText(me, "MemberValue", fieldValue);
1491 info.Node = WriteElement (me, "Docs");
1493 UpdateExtensionMethods (me, info);
1496 static readonly string[] ValidExtensionMembers = {
1505 static readonly string[] ValidExtensionDocMembers = {
1511 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1513 MethodDefinition me = info.Member as MethodDefinition;
1516 if (info.Parameters.Count < 1)
1518 if (!DocUtils.IsExtensionMethod (me))
1521 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1522 XmlNode member = e.CloneNode (true);
1523 em.AppendChild (member);
1524 RemoveExcept (member, ValidExtensionMembers);
1525 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1526 WriteElementText (member, "MemberType", "ExtensionMethod");
1527 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1528 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1529 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1530 member.AppendChild (link);
1531 AddTargets (em, info);
1533 extensionMethods.Add (em);
1536 private static void RemoveExcept (XmlNode node, string[] except)
1540 MyXmlNodeList remove = null;
1541 foreach (XmlNode n in node.ChildNodes) {
1542 if (Array.BinarySearch (except, n.Name) < 0) {
1544 remove = new MyXmlNodeList ();
1549 foreach (XmlNode n in remove)
1550 node.RemoveChild (n);
1553 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1555 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1556 member.PrependChild (targets);
1557 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1558 AppendElementAttributeText (targets, "Target", "Type",
1559 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1562 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1563 ConstraintCollection constraints = gp.Constraints;
1564 if (constraints.Count == 0)
1565 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1567 foreach (TypeReference c in constraints)
1568 AppendElementAttributeText(targets, "Target", "Type",
1569 slashdocFormatter.GetDeclaration (c));
1573 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1576 TypeDefinition type = DocUtils.GetTypeDefinition (field.DeclaringType);
1577 if (type != null && type.IsEnum) return false;
1579 if (type != null && type.IsGenericType ()) return false;
1580 if (!field.HasConstant)
1582 if (field.IsLiteral) {
1583 object val = field.Constant;
1584 if (val == null) value = "null";
1585 else if (val is Enum) value = val.ToString();
1586 else if (val is IFormattable) {
1587 value = ((IFormattable)val).ToString();
1589 value = "\"" + value + "\"";
1591 if (value != null && value != "")
1597 // XML HELPER FUNCTIONS
1599 private static XmlElement WriteElement(XmlNode parent, string element) {
1600 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1602 string[] path = element.Split('/');
1603 foreach (string p in path) {
1604 ret = (XmlElement)parent.SelectSingleNode(p);
1607 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1608 ename = ename.Substring(0, ename.IndexOf('['));
1609 ret = parent.OwnerDocument.CreateElement(ename);
1610 parent.AppendChild(ret);
1619 private static void WriteElementText(XmlNode parent, string element, string value) {
1620 XmlElement node = WriteElement(parent, element);
1621 node.InnerText = value;
1624 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1626 XmlElement n = parent.OwnerDocument.CreateElement (element);
1627 parent.AppendChild (n);
1628 n.InnerText = value;
1632 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1634 XmlElement n = parent.OwnerDocument.CreateElement (element);
1635 parent.AppendChild (n);
1636 n.SetAttribute (attribute, value);
1640 private static XmlNode CopyNode (XmlNode source, XmlNode dest)
1642 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1643 dest.AppendChild (copy);
1647 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1648 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1651 node = WriteElement(parent, element);
1652 node.InnerText = value;
1654 private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
1655 XmlElement node = WriteElement(parent, element);
1656 if (node.GetAttribute(attribute) == value) return;
1657 node.SetAttribute(attribute, value);
1659 private static void ClearElement(XmlElement parent, string name) {
1660 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1662 parent.RemoveChild(node);
1665 // DOCUMENTATION HELPER FUNCTIONS
1667 private void MakeDocNode (DocsNodeInfo info)
1669 List<GenericParameter> genericParams = info.GenericParameters;
1670 ParameterDefinitionCollection parameters = info.Parameters;
1671 TypeReference returntype = info.ReturnType;
1672 bool returnisreturn = info.ReturnIsReturn;
1673 XmlElement e = info.Node;
1674 bool addremarks = info.AddRemarks;
1676 WriteElementInitialText(e, "summary", "To be added.");
1678 if (parameters != null) {
1679 string[] values = new string [parameters.Count];
1680 for (int i = 0; i < values.Length; ++i)
1681 values [i] = parameters [i].Name;
1682 UpdateParameters (e, "param", values);
1685 if (genericParams != null) {
1686 string[] values = new string [genericParams.Count];
1687 for (int i = 0; i < values.Length; ++i)
1688 values [i] = genericParams [i].Name;
1689 UpdateParameters (e, "typeparam", values);
1692 string retnodename = null;
1693 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
1694 retnodename = returnisreturn ? "returns" : "value";
1695 string retnodename_other = !returnisreturn ? "returns" : "value";
1697 // If it has a returns node instead of a value node, change its name.
1698 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1699 if (retother != null) {
1700 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1701 foreach (XmlNode node in retother)
1702 retnode.AppendChild(node.CloneNode(true));
1703 e.ReplaceChild(retnode, retother);
1705 WriteElementInitialText(e, retnodename, "To be added.");
1708 ClearElement(e, "returns");
1709 ClearElement(e, "value");
1713 WriteElementInitialText(e, "remarks", "To be added.");
1715 if (exceptions.HasValue && info.Member != null) {
1716 UpdateExceptions (e, info.Member);
1719 if (info.EcmaDocs != null) {
1720 XmlReader r = info.EcmaDocs;
1721 int depth = r.Depth;
1722 r.ReadStartElement ("Docs");
1724 if (r.Name == "Docs") {
1725 if (r.Depth == depth && r.NodeType == XmlNodeType.EndElement)
1728 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
1730 if (!r.IsStartElement ())
1735 string name = r.GetAttribute ("name");
1738 XmlNode doc = e.SelectSingleNode (
1739 r.Name + "[@name='" + name + "']");
1740 string value = r.ReadInnerXml ();
1742 doc.InnerXml = value.Replace ("\r", "");
1749 string name = r.Name;
1750 string cref = r.GetAttribute ("cref");
1753 XmlNode doc = e.SelectSingleNode (
1754 r.Name + "[@cref='" + cref + "']");
1755 string value = r.ReadInnerXml ().Replace ("\r", "");
1757 doc.InnerXml = value;
1759 XmlElement n = e.OwnerDocument.CreateElement (name);
1760 n.SetAttribute ("cref", cref);
1767 string name = r.Name;
1768 string xpath = r.Name;
1769 StringList attributes = new StringList (r.AttributeCount);
1770 if (r.MoveToFirstAttribute ()) {
1772 attributes.Add ("@" + r.Name + "=\"" + r.Value + "\"");
1773 } while (r.MoveToNextAttribute ());
1776 if (attributes.Count > 0) {
1777 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
1779 XmlNode doc = e.SelectSingleNode (xpath);
1780 string value = r.ReadInnerXml ().Replace ("\r", "");
1782 doc.InnerXml = value;
1785 XmlElement n = e.OwnerDocument.CreateElement (name);
1787 foreach (string a in attributes) {
1788 int eq = a.IndexOf ('=');
1789 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
1798 if (info.SlashDocs != null) {
1799 XmlNode elem = info.SlashDocs;
1801 if (elem.SelectSingleNode("summary") != null)
1802 ClearElement(e, "summary");
1803 if (elem.SelectSingleNode("remarks") != null)
1804 ClearElement(e, "remarks");
1805 if (elem.SelectSingleNode("value") != null)
1806 ClearElement(e, "value");
1807 if (retnodename != null && elem.SelectSingleNode(retnodename) != null)
1808 ClearElement(e, retnodename);
1810 foreach (XmlNode child in elem.ChildNodes) {
1811 switch (child.Name) {
1814 XmlAttribute name = child.Attributes ["name"];
1817 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
1819 p2.InnerXml = child.InnerXml;
1824 case "permission": {
1825 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
1828 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
1830 a = e.OwnerDocument.CreateElement (child.Name);
1831 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
1834 a.InnerXml = child.InnerXml;
1838 XmlAttribute cref = child.Attributes ["cref"];
1841 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
1843 a = e.OwnerDocument.CreateElement ("altmember");
1844 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
1850 CopyNode (child, e);
1857 OrderDocsNodes (e, e.ChildNodes);
1858 NormalizeWhitespace(e);
1861 static readonly string[] DocsNodeOrder = {
1862 "typeparam", "param", "summary", "returns", "value", "remarks",
1865 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
1867 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1868 for (int i = 0; i < DocsNodeOrder.Length; ++i) {
1869 for (int j = 0; j < children.Count; ++j) {
1870 XmlNode c = children [j];
1871 if (c.Name == DocsNodeOrder [i]) {
1872 newChildren.Add (c);
1876 if (newChildren.Count >= 0)
1877 docs.PrependChild ((XmlNode) newChildren [0]);
1878 for (int i = 1; i < newChildren.Count; ++i) {
1879 XmlNode prev = (XmlNode) newChildren [i-1];
1880 XmlNode cur = (XmlNode) newChildren [i];
1881 docs.RemoveChild (cur);
1882 docs.InsertAfter (cur, prev);
1887 private void UpdateParameters (XmlElement e, string element, string[] values)
1889 if (values != null) {
1890 XmlNode[] paramnodes = new XmlNode[values.Length];
1892 // Some documentation had param nodes with leading spaces.
1893 foreach (XmlElement paramnode in e.SelectNodes(element)){
1894 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
1897 // If a member has only one parameter, we can track changes to
1898 // the name of the parameter easily.
1899 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
1900 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
1903 bool reinsert = false;
1905 // Pick out existing and still-valid param nodes, and
1906 // create nodes for parameters not in the file.
1907 Hashtable seenParams = new Hashtable();
1908 for (int pi = 0; pi < values.Length; pi++) {
1909 string p = values [pi];
1912 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
1913 if (paramnodes[pi] != null) continue;
1915 XmlElement pe = e.OwnerDocument.CreateElement(element);
1916 pe.SetAttribute("name", p);
1917 pe.InnerText = "To be added.";
1918 paramnodes[pi] = pe;
1922 // Remove parameters that no longer exist and check all params are in the right order.
1924 MyXmlNodeList todelete = new MyXmlNodeList ();
1925 foreach (XmlElement paramnode in e.SelectNodes(element)) {
1926 string name = paramnode.GetAttribute("name");
1927 if (!seenParams.ContainsKey(name)) {
1928 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1929 Warning ("The following param node can only be deleted if the --delete option is given: ");
1930 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
1932 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
1933 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1937 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
1938 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1939 e.ParentNode.Attributes ["MemberName"].Value,
1942 Warning ("\tValue={0}", paramnode.OuterXml);
1944 todelete.Add (paramnode);
1949 if ((int)seenParams[name] != idx)
1955 foreach (XmlNode n in todelete) {
1956 n.ParentNode.RemoveChild (n);
1959 // Re-insert the parameter nodes at the top of the doc section.
1961 for (int pi = values.Length-1; pi >= 0; pi--)
1962 e.PrependChild(paramnodes[pi]);
1964 // Clear all existing param nodes
1965 foreach (XmlNode paramnode in e.SelectNodes(element)) {
1966 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1967 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
1968 Console.WriteLine(paramnode.OuterXml);
1970 paramnode.ParentNode.RemoveChild(paramnode);
1976 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
1978 string existingName = pe.GetAttribute ("name");
1979 pe.SetAttribute ("name", newName);
1980 if (existingName == newName)
1982 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
1983 if (paramref.GetAttribute ("name").Trim () == existingName)
1984 paramref.SetAttribute ("name", newName);
1987 class CrefComparer : XmlNodeComparer {
1989 public CrefComparer ()
1993 public override int Compare (XmlNode x, XmlNode y)
1995 string xType = x.Attributes ["cref"].Value;
1996 string yType = y.Attributes ["cref"].Value;
1997 string xNamespace = GetNamespace (xType);
1998 string yNamespace = GetNamespace (yType);
2000 int c = xNamespace.CompareTo (yNamespace);
2003 return xType.CompareTo (yType);
2006 static string GetNamespace (string type)
2008 int n = type.LastIndexOf ('.');
2010 return type.Substring (0, n);
2011 return string.Empty;
2015 private void UpdateExceptions (XmlNode docs, IMemberReference member)
2017 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
2018 string cref = slashdocFormatter.GetDeclaration (source.Exception);
2019 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
2022 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
2023 e.SetAttribute ("cref", cref);
2024 e.InnerXml = "To be added; from: <see cref=\"" +
2025 string.Join ("\" />, <see cref=\"",
2026 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
2029 docs.AppendChild (e);
2031 SortXmlNodes (docs, docs.SelectNodes ("exception"),
2032 new CrefComparer ());
2035 private static void NormalizeWhitespace(XmlElement e) {
2036 // Remove all text and whitespace nodes from the element so it
2037 // is outputted with nice indentation and no blank lines.
2038 ArrayList deleteNodes = new ArrayList();
2039 foreach (XmlNode n in e)
2040 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2042 foreach (XmlNode n in deleteNodes)
2043 n.ParentNode.RemoveChild(n);
2046 private static bool UpdateAssemblyVersions (XmlElement root, IMemberReference member, bool add)
2048 TypeDefinition type = member as TypeDefinition;
2050 type = member.DeclaringType as TypeDefinition;
2051 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
2054 private static string GetAssemblyVersion (AssemblyDefinition assembly)
2056 return assembly.Name.Version.ToString();
2059 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
2061 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
2063 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2064 root.AppendChild(e);
2066 MyXmlNodeList matches = new MyXmlNodeList (assemblyVersions.Length);
2067 foreach (XmlElement v in e.SelectNodes ("AssemblyVersion")) {
2068 foreach (string sv in assemblyVersions)
2069 if (v.InnerText == sv)
2072 // matches.Count > 0 && add: ignore -- already present
2073 if (matches.Count > 0 && !add) {
2074 foreach (XmlNode c in matches)
2077 else if (matches.Count == 0 && add) {
2078 foreach (string sv in assemblyVersions) {
2079 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2084 // matches.Count == 0 && !add: ignore -- already not present
2086 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2087 SortXmlNodes (e, avs, new VersionComparer ());
2089 return avs.Count != 0;
2092 // FIXME: get TypeReferences instead of string comparison?
2093 private static string[] IgnorableAttributes = {
2094 // Security related attributes
2095 "System.Reflection.AssemblyKeyFileAttribute",
2096 "System.Reflection.AssemblyDelaySignAttribute",
2097 // Present in @RefType
2098 "System.Runtime.InteropServices.OutAttribute",
2099 // For naming the indexer to use when not using indexers
2100 "System.Reflection.DefaultMemberAttribute",
2101 // for decimal constants
2102 "System.Runtime.CompilerServices.DecimalConstantAttribute",
2103 // compiler generated code
2104 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
2105 // more compiler generated code, e.g. iterator methods
2106 "System.Diagnostics.DebuggerHiddenAttribute",
2107 "System.Runtime.CompilerServices.FixedBufferAttribute",
2108 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
2109 // extension methods
2110 "System.Runtime.CompilerServices.ExtensionAttribute",
2113 private void MakeAttributes (XmlElement root, CustomAttributeCollection attributes, bool assemblyAttributes)
2115 if (attributes.Count == 0) {
2116 ClearElement(root, "Attributes");
2121 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2125 e = root.OwnerDocument.CreateElement("Attributes");
2127 foreach (CustomAttribute attribute in attributes.Cast<CustomAttribute> ()
2128 .OrderBy (ca => ca.Constructor.DeclaringType.FullName)) {
2129 if (!attribute.Resolve ()) {
2131 Warning ("warning: could not resolve type {0}.",
2132 attribute.Constructor.DeclaringType.FullName);
2134 TypeDefinition attrType = attribute.Constructor.DeclaringType as TypeDefinition;
2135 if (attrType != null && !IsPublic (attrType))
2137 if (slashdocFormatter.GetName (attribute.Constructor.DeclaringType) == null)
2140 if (Array.IndexOf (IgnorableAttributes, attribute.Constructor.DeclaringType.FullName) >= 0)
2145 StringList fields = new StringList ();
2147 ParameterDefinitionCollection parameters = attribute.Constructor.Parameters;
2148 for (int i = 0; i < attribute.ConstructorParameters.Count; ++i) {
2149 fields.Add (MakeAttributesValueString (
2150 attribute.ConstructorParameters [i],
2151 parameters [i].ParameterType));
2154 (from de in attribute.Fields.Cast<DictionaryEntry> ()
2155 select new { Type=attribute.GetFieldType (de.Key.ToString ()), Name=de.Key, Value=de.Value })
2157 (from de in attribute.Properties.Cast<DictionaryEntry> ()
2158 select new { Type=attribute.GetPropertyType (de.Key.ToString ()), Name=de.Key, Value=de.Value }))
2159 .OrderBy (v => v.Name);
2160 foreach (var d in namedArgs)
2161 fields.Add (string.Format ("{0}={1}", d.Name,
2162 MakeAttributesValueString (d.Value, d.Type)));
2164 string a2 = String.Join(", ", fields.ToArray ());
2165 if (a2 != "") a2 = "(" + a2 + ")";
2167 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2170 string name = attribute.Constructor.DeclaringType.FullName;
2171 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
2172 WriteElementText(ae, "AttributeName", name + a2);
2175 if (b && e.ParentNode == null)
2176 root.AppendChild(e);
2178 ClearElement(root, "Attributes");
2180 NormalizeWhitespace(e);
2183 private static string MakeAttributesValueString (object v, TypeReference valueType)
2187 if (valueType.FullName == "System.Type")
2188 return "typeof(" + v.ToString () + ")";
2189 if (valueType.FullName == "System.String")
2190 return "\"" + v.ToString () + "\"";
2192 return (bool)v ? "true" : "false";
2193 TypeDefinition valueDef = DocUtils.GetTypeDefinition (valueType);
2194 if (valueDef == null || !valueDef.IsEnum)
2195 return v.ToString ();
2196 string typename = GetDocTypeFullName (valueType);
2197 var values = GetEnumerationValues (valueDef);
2198 ulong c = Convert.ToUInt64 (v);
2199 if (values.ContainsKey (c))
2200 return typename + "." + values [c];
2201 if (valueDef.CustomAttributes.Cast<CustomAttribute> ()
2202 .Any (ca => ca.Constructor.DeclaringType.FullName == "System.FlagsAttribute")) {
2203 return string.Join (" | ",
2204 (from i in values.Keys
2206 select typename + "." + values [i])
2209 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
2212 private static Dictionary<ulong, string> GetEnumerationValues (TypeDefinition type)
2215 (from f in type.Fields.Cast<FieldDefinition> ()
2216 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
2218 .ToDictionary (f => Convert.ToUInt64 (f.Constant), f => f.Name);
2221 private void MakeParameters (XmlElement root, ParameterDefinitionCollection parameters)
2223 XmlElement e = WriteElement(root, "Parameters");
2225 foreach (ParameterDefinition p in parameters) {
2226 XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
2228 pe.SetAttribute("Name", p.Name);
2229 pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
2230 if (p.ParameterType is ReferenceType) {
2231 if (p.IsOut) pe.SetAttribute("RefType", "out");
2232 else pe.SetAttribute("RefType", "ref");
2234 MakeAttributes (pe, p.CustomAttributes, false);
2238 private void MakeTypeParameters (XmlElement root, GenericParameterCollection typeParams)
2240 if (typeParams == null || typeParams.Count == 0) {
2241 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2243 root.RemoveChild (f);
2246 XmlElement e = WriteElement(root, "TypeParameters");
2248 foreach (GenericParameter t in typeParams) {
2249 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2251 pe.SetAttribute("Name", t.Name);
2252 MakeAttributes (pe, t.CustomAttributes, false);
2253 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2254 ConstraintCollection constraints = t.Constraints;
2255 GenericParameterAttributes attrs = t.Attributes;
2256 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2264 ce = root.OwnerDocument.CreateElement ("Constraints");
2266 pe.AppendChild (ce);
2267 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2268 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2269 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2270 AppendElementText (ce, "ParameterAttribute", "Covariant");
2271 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2272 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2273 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2274 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2275 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2276 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2277 foreach (TypeReference c in constraints) {
2278 TypeDefinition cd = DocUtils.GetTypeDefinition (c);
2279 AppendElementText (ce,
2280 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2281 GetDocTypeFullName (c));
2286 private void MakeParameters (XmlElement root, IMemberReference mi)
2288 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2289 MakeParameters (root, ((MethodDefinition)mi).Parameters);
2290 else if (mi is MethodDefinition) {
2291 MethodDefinition mb = (MethodDefinition) mi;
2292 ParameterDefinitionCollection parameters = mb.Parameters;
2293 MakeParameters(root, parameters);
2294 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2295 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2296 p.SetAttribute ("RefType", "this");
2299 else if (mi is PropertyDefinition) {
2300 ParameterDefinitionCollection parameters = ((PropertyDefinition)mi).Parameters;
2301 if (parameters.Count > 0)
2302 MakeParameters(root, parameters);
2306 else if (mi is FieldDefinition) return;
2307 else if (mi is EventDefinition) return;
2308 else throw new ArgumentException();
2311 private static string GetDocParameterType (TypeReference type)
2313 return GetDocTypeFullName (type).Replace ("@", "&");
2316 private void MakeReturnValue (XmlElement root, TypeReference type, CustomAttributeCollection attributes)
2318 XmlElement e = WriteElement(root, "ReturnValue");
2320 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2321 if (attributes != null)
2322 MakeAttributes(e, attributes, false);
2325 private void MakeReturnValue (XmlElement root, IMemberReference mi)
2327 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2329 else if (mi is MethodDefinition)
2330 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType.ReturnType, ((MethodDefinition)mi).ReturnType.CustomAttributes);
2331 else if (mi is PropertyDefinition)
2332 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
2333 else if (mi is FieldDefinition)
2334 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
2335 else if (mi is EventDefinition)
2336 MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
2338 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2341 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2343 IMemberReference mi = info.Member;
2344 if (mi is TypeDefinition) return null;
2346 string sigs = MakeMemberSignature(mi);
2347 if (sigs == null) return null; // not publicly visible
2349 // no documentation for property/event accessors. Is there a better way of doing this?
2350 if (mi.Name.StartsWith("get_")) return null;
2351 if (mi.Name.StartsWith("set_")) return null;
2352 if (mi.Name.StartsWith("add_")) return null;
2353 if (mi.Name.StartsWith("remove_")) return null;
2354 if (mi.Name.StartsWith("raise_")) return null;
2356 XmlElement me = doc.CreateElement("Member");
2357 me.SetAttribute("MemberName", GetMemberName (mi));
2362 if (since != null) {
2363 XmlNode docs = me.SelectSingleNode("Docs");
2364 docs.AppendChild (CreateSinceNode (doc));
2370 private static string GetMemberName (IMemberReference mi)
2372 MethodDefinition mb = mi as MethodDefinition;
2374 PropertyDefinition pi = mi as PropertyDefinition;
2377 return DocUtils.GetPropertyName (pi);
2379 StringBuilder sb = new StringBuilder (mi.Name.Length);
2380 if (!DocUtils.IsExplicitlyImplemented (mb))
2381 sb.Append (mi.Name);
2383 TypeReference iface;
2384 MethodReference ifaceMethod;
2385 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2386 sb.Append (GetDocTypeFullName (iface));
2388 sb.Append (ifaceMethod.Name);
2390 if (mb.IsGenericMethod ()) {
2391 GenericParameterCollection typeParams = mb.GenericParameters;
2392 if (typeParams.Count > 0) {
2394 sb.Append (typeParams [0].Name);
2395 for (int i = 1; i < typeParams.Count; ++i)
2396 sb.Append (",").Append (typeParams [i].Name);
2400 return sb.ToString ();
2403 private static int CountChars (string s, char c)
2406 for (int i = 0; i < s.Length; ++i) {
2413 /// SIGNATURE GENERATION FUNCTIONS
2415 static string MakeTypeSignature (TypeReference type)
2417 return csharpFormatter.GetDeclaration (type);
2420 static string MakeMemberSignature (IMemberReference mi)
2422 return csharpFullFormatter.GetDeclaration (mi);
2425 static string GetMemberType (IMemberReference mi)
2427 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2428 return "Constructor";
2429 if (mi is MethodDefinition)
2431 if (mi is PropertyDefinition)
2433 if (mi is FieldDefinition)
2435 if (mi is EventDefinition)
2437 throw new ArgumentException();
2440 private static string GetDocTypeName (TypeReference type)
2442 return docTypeFormatter.GetName (type);
2445 private static string GetDocTypeFullName (TypeReference type)
2447 return DocTypeFullMemberFormatter.Default.GetName (type);
2450 class DocsNodeInfo {
2451 public DocsNodeInfo (XmlElement node)
2456 public DocsNodeInfo (XmlElement node, TypeDefinition type)
2462 public DocsNodeInfo (XmlElement node, IMemberReference member)
2465 SetMemberInfo (member);
2468 void SetType (TypeDefinition type)
2471 throw new ArgumentNullException ("type");
2472 GenericParameters = new List<GenericParameter> (type.GenericParameters.Cast<GenericParameter> ());
2473 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
2474 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
2475 for (int i = 0; i < declTypes.Count - 1; ++i) {
2476 int remove = System.Math.Min (maxGenArgs,
2477 DocUtils.GetGenericArgumentCount (declTypes [i]));
2478 maxGenArgs -= remove;
2479 while (remove-- > 0)
2480 GenericParameters.RemoveAt (0);
2482 if (DocUtils.IsDelegate (type)) {
2483 Parameters = type.GetMethod("Invoke").Parameters;
2484 ReturnType = type.GetMethod("Invoke").ReturnType.ReturnType;
2486 SetSlashDocs (type);
2489 void SetMemberInfo (IMemberReference member)
2492 throw new ArgumentNullException ("member");
2493 ReturnIsReturn = true;
2497 if (member is MethodReference ) {
2498 MethodReference mr = (MethodReference) member;
2499 Parameters = mr.Parameters;
2500 if (mr.IsGenericMethod ()) {
2501 GenericParameters = new List<GenericParameter> (mr.GenericParameters.Cast<GenericParameter> ());
2504 else if (member is PropertyDefinition) {
2505 Parameters = ((PropertyDefinition) member).Parameters;
2508 if (member is MethodDefinition) {
2509 ReturnType = ((MethodDefinition) member).ReturnType.ReturnType;
2510 } else if (member is PropertyDefinition) {
2511 ReturnType = ((PropertyDefinition) member).PropertyType;
2512 ReturnIsReturn = false;
2515 // no remarks section for enum members
2516 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
2518 SetSlashDocs (member);
2521 private void SetSlashDocs (IMemberReference member)
2523 if (slashdocs == null)
2526 string slashdocsig = slashdocFormatter.GetDeclaration (member);
2527 if (slashdocsig != null)
2528 SlashDocs = slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
2531 public TypeReference ReturnType;
2532 public List<GenericParameter> GenericParameters;
2533 public ParameterDefinitionCollection Parameters;
2534 public bool ReturnIsReturn;
2535 public XmlElement Node;
2536 public bool AddRemarks = true;
2537 public XmlNode SlashDocs;
2538 public XmlReader EcmaDocs;
2539 public IMemberReference Member;
2542 static string GetXPathForMember (DocumentationMember member)
2544 StringBuilder xpath = new StringBuilder ();
2545 xpath.Append ("//Members/Member[@MemberName=\"")
2546 .Append (member.MemberName)
2548 if (member.Parameters != null && member.Parameters.Count > 0) {
2549 xpath.Append ("/Parameters[count(Parameter) = ")
2550 .Append (member.Parameters.Count);
2551 for (int i = 0; i < member.Parameters.Count; ++i) {
2552 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2553 xpath.Append (member.Parameters [i]);
2554 xpath.Append ("\"");
2556 xpath.Append ("]/..");
2558 return xpath.ToString ();
2561 public static string GetXPathForMember (XPathNavigator member)
2563 StringBuilder xpath = new StringBuilder ();
2564 xpath.Append ("//Type[@FullName=\"")
2565 .Append (member.SelectSingleNode ("../../@FullName").Value)
2567 xpath.Append ("Members/Member[@MemberName=\"")
2568 .Append (member.SelectSingleNode ("@MemberName").Value)
2570 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2571 if (parameters.Count > 0) {
2572 xpath.Append ("/Parameters[count(Parameter) = ")
2573 .Append (parameters.Count);
2575 while (parameters.MoveNext ()) {
2577 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2578 xpath.Append (parameters.Current.Value);
2579 xpath.Append ("\"");
2581 xpath.Append ("]/..");
2583 return xpath.ToString ();
2586 public static string GetXPathForMember (IMemberReference member)
2588 StringBuilder xpath = new StringBuilder ();
2589 xpath.Append ("//Type[@FullName=\"")
2590 .Append (member.DeclaringType.FullName)
2592 xpath.Append ("Members/Member[@MemberName=\"")
2593 .Append (GetMemberName (member))
2596 ParameterDefinitionCollection parameters = null;
2597 if (member is MethodDefinition)
2598 parameters = ((MethodDefinition) member).Parameters;
2599 else if (member is PropertyDefinition) {
2600 parameters = ((PropertyDefinition) member).Parameters;
2602 if (parameters != null && parameters.Count > 0) {
2603 xpath.Append ("/Parameters[count(Parameter) = ")
2604 .Append (parameters.Count);
2605 for (int i = 0; i < parameters.Count; ++i) {
2606 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2607 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2608 xpath.Append ("\"");
2610 xpath.Append ("]/..");
2612 return xpath.ToString ();
2616 static class CecilExtensions {
2617 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type)
2619 foreach (var c in type.Constructors)
2620 yield return (IMemberReference) c;
2621 foreach (var e in type.Events)
2622 yield return (IMemberReference) e;
2623 foreach (var f in type.Fields)
2624 yield return (IMemberReference) f;
2625 foreach (var m in type.Methods)
2626 yield return (IMemberReference) m;
2627 foreach (var t in type.NestedTypes)
2628 yield return (IMemberReference) t;
2629 foreach (var p in type.Properties)
2630 yield return (IMemberReference) p;
2633 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type, string member)
2635 return GetMembers (type).Where (m => m.Name == member);
2638 public static IMemberReference GetMember (this TypeDefinition type, string member)
2640 return GetMembers (type, member).EnsureZeroOrOne ();
2643 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2645 if (source.Count () > 1)
2646 throw new InvalidOperationException ("too many matches");
2647 return source.FirstOrDefault ();
2650 static T EnsureOne<T> (this IEnumerable<T> source)
2652 if (source.Count () > 1)
2653 throw new InvalidOperationException ("too many matches: " +
2654 string.Join ("; ", source.Select (e => e.ToString ()).ToArray ()));
2655 return source.First ();
2658 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2660 return type.Methods.Cast<MethodDefinition> ()
2661 .Where (m => m.Name == method)
2662 .EnsureZeroOrOne ();
2665 public static IEnumerable<IMemberReference> GetDefaultMembers (this TypeReference type)
2667 TypeDefinition def = type as TypeDefinition;
2669 return new IMemberReference [0];
2670 CustomAttribute defMemberAttr = type.CustomAttributes.Cast<CustomAttribute> ()
2671 .Where (c => c.Constructor.DeclaringType.FullName == "System.Reflection.DefaultMemberAttribute")
2673 if (defMemberAttr == null)
2674 return new IMemberReference [0];
2675 string name = (string) defMemberAttr.ConstructorParameters [0];
2676 return def.Properties.Cast<PropertyDefinition> ()
2677 .Where (p => p.Name == name)
2678 .Select (p => (IMemberReference) p);
2681 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2683 return assembly.Modules.Cast<ModuleDefinition> ()
2684 .SelectMany (md => md.Types.Cast<TypeDefinition> ());
2687 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2689 return GetTypes (assembly)
2690 .Where (td => td.FullName == type)
2691 .EnsureZeroOrOne ();
2694 public static bool IsGenericType (this TypeReference type)
2696 return type.GenericParameters.Count > 0;
2699 public static bool IsGenericMethod (this MethodReference method)
2701 return method.GenericParameters.Count > 0;
2704 public static IMemberReference GetDefinition (this IMemberReference member)
2706 EventReference er = member as EventReference;
2708 return GetEventDefinition (er);
2709 FieldReference fr = member as FieldReference;
2711 return GetFieldDefinition (fr);
2712 MethodReference mr = member as MethodReference;
2714 return GetMethodDefinition (mr);
2715 PropertyReference pr = member as PropertyReference;
2717 return GetPropertyDefinition (pr);
2718 TypeReference tr = member as TypeReference;
2720 return GetTypeDefinition (tr);
2721 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2724 public static EventDefinition GetEventDefinition (this EventReference ev)
2726 EventDefinition evDef = ev as EventDefinition;
2729 return (EventDefinition) ev.DeclaringType.GetTypeDefinition ()
2730 .GetMember (ev.Name);
2733 public static FieldDefinition GetFieldDefinition (this FieldReference field)
2735 FieldDefinition fieldDef = field as FieldDefinition;
2736 if (fieldDef != null)
2738 return (FieldDefinition) field.DeclaringType.GetTypeDefinition ()
2739 .GetMember (field.Name);
2742 public static MethodDefinition GetMethodDefinition (this MethodReference method)
2744 MethodDefinition methodDef = method as MethodDefinition;
2745 if (methodDef != null)
2747 method = method.GetOriginalMethod ();
2748 return method.DeclaringType.GetTypeDefinition ()
2749 .GetMembers (method.Name).OfType<MethodDefinition> ()
2751 AreSame (method.ReturnType.ReturnType, m.ReturnType.ReturnType) &&
2752 AreSame (method.Parameters, m.Parameters))
2756 static bool AreSame (ParameterDefinitionCollection a, ParameterDefinitionCollection b)
2758 if (a.Count != b.Count)
2764 for (int i = 0; i < a.Count; i++) {
2765 if (!AreSame (a [i].ParameterType, b [i].ParameterType))
2772 static bool AreSame (TypeReference a, TypeReference b)
2774 while (a is TypeSpecification || b is TypeSpecification) {
2775 if (a.GetType () != b.GetType ())
2778 IGenericInstance ga = a as IGenericInstance;
2779 IGenericInstance gb = b as IGenericInstance;
2781 a = ((TypeSpecification) a).ElementType;
2782 b = ((TypeSpecification) b).ElementType;
2784 if (ga != null && gb != null) {
2785 if (ga.GenericArguments.Count != gb.GenericArguments.Count) {
2788 for (int i = 0; i < ga.GenericArguments.Count; ++i) {
2789 if (!AreSame (ga.GenericArguments [i], gb.GenericArguments [i]))
2795 GenericParameter pa = (a as GenericParameter);
2796 GenericParameter pb = (b as GenericParameter);
2797 if ((pa != null) || (pb != null)) {
2798 if (a.GetType () != b.GetType ())
2801 return pa.Position == pb.Position;
2804 return a.FullName == b.FullName;
2807 public static PropertyDefinition GetPropertyDefinition (this PropertyReference property)
2809 PropertyDefinition propertyDef = property as PropertyDefinition;
2810 if (propertyDef != null)
2812 return (PropertyDefinition) property.DeclaringType.GetTypeDefinition ()
2813 .GetMembers (property.Name).OfType<PropertyDefinition> ()
2814 .Where (p => p.PropertyType.FullName == property.PropertyType.FullName &&
2815 AreSame (property.Parameters, p.Parameters))
2819 public static TypeDefinition GetTypeDefinition (this TypeReference type)
2821 return DocUtils.GetTypeDefinition (type);
2825 static class DocUtils {
2826 public static bool IsExplicitlyImplemented (MethodDefinition method)
2828 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2831 public static string GetTypeDotMember (string name)
2833 int startType, startMethod;
2834 startType = startMethod = -1;
2835 for (int i = 0; i < name.Length; ++i) {
2836 if (name [i] == '.') {
2837 startType = startMethod;
2841 return name.Substring (startType+1);
2844 public static string GetMember (string name)
2846 int i = name.LastIndexOf ('.');
2849 return name.Substring (i+1);
2852 public static void GetInfoForExplicitlyImplementedMethod (
2853 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2857 if (method.Overrides.Count != 1)
2858 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2859 iface = method.Overrides [0].DeclaringType;
2860 ifaceMethod = method.Overrides [0];
2863 public static string GetPropertyName (PropertyDefinition pi)
2865 // Issue: (g)mcs-generated assemblies that explicitly implement
2866 // properties don't specify the full namespace, just the
2867 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2868 MethodDefinition method = pi.GetMethod;
2870 method = pi.SetMethod;
2871 if (!IsExplicitlyImplemented (method))
2874 // Need to determine appropriate namespace for this member.
2875 TypeReference iface;
2876 MethodReference ifaceMethod;
2877 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2878 return string.Join (".", new string[]{
2879 DocTypeFullMemberFormatter.Default.GetName (iface),
2880 GetMember (pi.Name)});
2883 public static string GetNamespace (TypeReference type)
2885 if (type.GetOriginalType ().IsNested)
2886 type = type.GetOriginalType ();
2887 while (type != null && type.IsNested)
2888 type = type.DeclaringType;
2890 return string.Empty;
2891 return type.Namespace;
2894 public static string PathCombine (string dir, string path)
2900 return Path.Combine (dir, path);
2903 public static bool IsExtensionMethod (MethodDefinition method)
2906 method.CustomAttributes.Cast<CustomAttribute> ()
2907 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2909 method.DeclaringType.CustomAttributes.Cast<CustomAttribute> ()
2910 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2914 public static bool IsDelegate (TypeDefinition type)
2916 TypeReference baseRef = type.BaseType;
2917 if (baseRef == null)
2919 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2920 baseRef.FullName == "System.MulticastDelegate";
2923 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2925 List<TypeReference> decls = new List<TypeReference> ();
2927 while (type.DeclaringType != null) {
2928 decls.Add (type.DeclaringType);
2929 type = type.DeclaringType;
2935 public static int GetGenericArgumentCount (TypeReference type)
2937 GenericInstanceType inst = type as GenericInstanceType;
2939 ? inst.GenericArguments.Count
2940 : type.GenericParameters.Count;
2943 public static TypeDefinition GetTypeDefinition (TypeReference type)
2945 // Remove generic instantiation info (so string comparison below works)
2946 type = type.GetOriginalType ();
2947 TypeDefinition typeDef = type as TypeDefinition;
2948 if (typeDef != null)
2951 AssemblyNameReference reference = type.Scope as AssemblyNameReference;
2952 if (reference != null) {
2953 AssemblyDefinition ad = type.Module.Assembly.Resolver.Resolve (reference);
2954 if (ad != null && (typeDef = ad.MainModule.Types [type.FullName]) != null)
2957 ModuleDefinition module = type.Scope as ModuleDefinition;
2958 if (module != null && (typeDef = module.Types [type.FullName]) != null)
2963 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
2965 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
2966 List<TypeReference> userInterfaces = new List<TypeReference> ();
2967 foreach (TypeReference iface in type.Interfaces) {
2968 TypeReference lookup = GetTypeDefinition (iface) ?? iface;
2969 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
2970 userInterfaces.Add (iface);
2972 return userInterfaces;
2975 private static string GetQualifiedTypeName (TypeReference type)
2977 return "[" + type.Scope.Name + "]" + type.FullName;
2980 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
2982 HashSet<string> inheritedInterfaces = new HashSet<string> ();
2983 Action<TypeDefinition> a = null;
2985 if (t == null) return;
2986 foreach (TypeReference r in t.Interfaces) {
2987 inheritedInterfaces.Add (GetQualifiedTypeName (r));
2988 a (GetTypeDefinition (r));
2991 TypeReference baseRef = type.BaseType;
2992 while (baseRef != null) {
2993 TypeDefinition baseDef = GetTypeDefinition (baseRef);
2994 if (baseDef != null) {
2996 baseRef = baseDef.BaseType;
3001 foreach (TypeReference r in type.Interfaces)
3002 a (GetTypeDefinition (r));
3003 return inheritedInterfaces;
3007 class DocumentationMember {
3008 public StringToStringMap MemberSignatures = new StringToStringMap ();
3009 public string ReturnType;
3010 public StringList Parameters;
3011 public string MemberName;
3012 public string MemberType;
3014 public DocumentationMember (XmlReader reader)
3016 MemberName = reader.GetAttribute ("MemberName");
3017 int depth = reader.Depth;
3019 StringList p = new StringList ();
3021 if (reader.NodeType != XmlNodeType.Element)
3023 switch (reader.Name) {
3024 case "MemberSignature":
3025 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3028 MemberType = reader.ReadElementString ();
3031 if (reader.Depth == depth + 2)
3032 ReturnType = reader.ReadElementString ();
3035 if (reader.Depth == depth + 2)
3036 p.Add (reader.GetAttribute ("Type"));
3039 if (reader.Depth == depth + 1)
3043 } while (go && reader.Read () && reader.Depth >= depth);
3049 public DocumentationMember (XmlNode node)
3051 MemberName = node.Attributes ["MemberName"].Value;
3052 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3053 XmlAttribute l = n.Attributes ["Language"];
3054 XmlAttribute v = n.Attributes ["Value"];
3055 if (l != null && v != null)
3056 MemberSignatures [l.Value] = v.Value;
3058 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3059 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
3061 ReturnType = rt.InnerText;
3062 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
3064 Parameters = new StringList (p.Count);
3065 for (int i = 0; i < p.Count; ++i)
3066 Parameters.Add (p [i].Attributes ["Type"].Value);
3071 public enum MemberFormatterState {
3074 WithinGenericTypeContainer,
3077 public abstract class MemberFormatter {
3078 public virtual string GetName (IMemberReference member)
3080 TypeReference type = member as TypeReference;
3082 return GetTypeName (type);
3083 MethodReference method = member as MethodReference;
3084 if (method != null && method.Name == ".ctor") // method.IsConstructor
3085 return GetConstructorName (method);
3087 return GetMethodName (method);
3088 PropertyReference prop = member as PropertyReference;
3090 return GetPropertyName (prop);
3091 FieldReference field = member as FieldReference;
3093 return GetFieldName (field);
3094 EventReference e = member as EventReference;
3096 return GetEventName (e);
3097 throw new NotSupportedException ("Can't handle: " +
3098 (member == null ? "null" : member.GetType().ToString()));
3101 protected virtual string GetTypeName (TypeReference type)
3104 throw new ArgumentNullException ("type");
3105 return _AppendTypeName (new StringBuilder (type.Name.Length), type).ToString ();
3108 protected virtual char[] ArrayDelimeters {
3109 get {return new char[]{'[', ']'};}
3112 protected virtual MemberFormatterState MemberFormatterState { get; set; }
3114 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type)
3116 if (type is ArrayType) {
3117 TypeSpecification spec = type as TypeSpecification;
3118 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3119 .Append (ArrayDelimeters [0]);
3120 var origState = MemberFormatterState;
3121 MemberFormatterState = MemberFormatterState.WithinArray;
3122 ArrayType array = (ArrayType) type;
3123 int rank = array.Rank;
3125 buf.Append (new string (',', rank-1));
3126 MemberFormatterState = origState;
3127 return buf.Append (ArrayDelimeters [1]);
3129 if (type is ReferenceType) {
3130 return AppendRefTypeName (buf, type);
3132 if (type is PointerType) {
3133 return AppendPointerTypeName (buf, type);
3135 AppendNamespace (buf, type);
3136 if (type is GenericParameter) {
3137 return AppendTypeName (buf, type);
3139 GenericInstanceType genInst = type as GenericInstanceType;
3140 if (type.GenericParameters.Count == 0 &&
3141 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3142 return AppendFullTypeName (buf, type);
3144 return AppendGenericType (buf, type);
3147 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3149 string ns = DocUtils.GetNamespace (type);
3150 if (ns != null && ns.Length > 0)
3151 buf.Append (ns).Append ('.');
3155 private StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type)
3157 if (type.DeclaringType != null)
3158 AppendFullTypeName (buf, type.DeclaringType).Append (NestedTypeSeparator);
3159 return AppendTypeName (buf, type);
3162 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3164 return AppendTypeName (buf, type.Name);
3167 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3169 int n = typename.IndexOf ("`");
3171 return buf.Append (typename.Substring (0, n));
3172 return buf.Append (typename);
3175 protected virtual string RefTypeModifier {
3179 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type)
3181 TypeSpecification spec = type as TypeSpecification;
3182 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3183 .Append (RefTypeModifier);
3186 protected virtual string PointerModifier {
3190 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type)
3192 TypeSpecification spec = type as TypeSpecification;
3193 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3194 .Append (PointerModifier);
3197 protected virtual char[] GenericTypeContainer {
3198 get {return new char[]{'<', '>'};}
3201 protected virtual char NestedTypeSeparator {
3205 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3207 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3208 type is GenericInstanceType ? type.GetOriginalType () : type);
3209 List<TypeReference> genArgs = GetGenericArguments (type);
3212 bool insertNested = false;
3213 foreach (var decl in decls) {
3214 TypeReference declDef = DocUtils.GetTypeDefinition (decl) ?? decl;
3216 buf.Append (NestedTypeSeparator);
3218 insertNested = true;
3219 AppendTypeName (buf, declDef);
3220 int ac = DocUtils.GetGenericArgumentCount (declDef);
3224 buf.Append (GenericTypeContainer [0]);
3225 var origState = MemberFormatterState;
3226 MemberFormatterState = MemberFormatterState.WithinGenericTypeContainer;
3227 _AppendTypeName (buf, genArgs [argIdx++]);
3228 for (int i = 1; i < c; ++i)
3229 _AppendTypeName (buf.Append (","), genArgs [argIdx++]);
3230 MemberFormatterState = origState;
3231 buf.Append (GenericTypeContainer [1]);
3237 private List<TypeReference> GetGenericArguments (TypeReference type)
3239 var args = new List<TypeReference> ();
3240 GenericInstanceType inst = type as GenericInstanceType;
3242 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
3244 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
3248 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3253 protected virtual string GetConstructorName (MethodReference constructor)
3255 return constructor.Name;
3258 protected virtual string GetMethodName (MethodReference method)
3263 protected virtual string GetPropertyName (PropertyReference property)
3265 return property.Name;
3268 protected virtual string GetFieldName (FieldReference field)
3273 protected virtual string GetEventName (EventReference e)
3278 public virtual string GetDeclaration (IMemberReference member)
3281 throw new ArgumentNullException ("member");
3282 TypeDefinition type = member as TypeDefinition;
3284 return GetTypeDeclaration (type);
3285 MethodDefinition method = member as MethodDefinition;
3286 if (method != null && method.IsConstructor)
3287 return GetConstructorDeclaration (method);
3289 return GetMethodDeclaration (method);
3290 PropertyDefinition prop = member as PropertyDefinition;
3292 return GetPropertyDeclaration (prop);
3293 FieldDefinition field = member as FieldDefinition;
3295 return GetFieldDeclaration (field);
3296 EventDefinition e = member as EventDefinition;
3298 return GetEventDeclaration (e);
3299 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3302 protected virtual string GetTypeDeclaration (TypeDefinition type)
3305 throw new ArgumentNullException ("type");
3306 StringBuilder buf = new StringBuilder (type.Name.Length);
3307 _AppendTypeName (buf, type);
3308 AppendGenericTypeConstraints (buf, type);
3309 return buf.ToString ();
3312 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
3314 return GetConstructorName (constructor);
3317 protected virtual string GetMethodDeclaration (MethodDefinition method)
3319 // Special signature for destructors.
3320 if (method.Name == "Finalize" && method.Parameters.Count == 0)
3321 return GetFinalizerName (method);
3323 StringBuilder buf = new StringBuilder ();
3325 AppendVisibility (buf, method);
3326 if (buf.Length == 0 &&
3327 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3330 AppendModifiers (buf, method);
3332 if (buf.Length != 0)
3334 buf.Append (GetName (method.ReturnType.ReturnType)).Append (" ");
3336 AppendMethodName (buf, method);
3337 AppendGenericMethod (buf, method).Append (" ");
3338 AppendParameters (buf, method, method.Parameters);
3339 AppendGenericMethodConstraints (buf, method);
3340 return buf.ToString ();
3343 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3345 return buf.Append (method.Name);
3348 protected virtual string GetFinalizerName (MethodDefinition method)
3353 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3358 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3363 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3368 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3373 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3378 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
3380 return GetPropertyName (property);
3383 protected virtual string GetFieldDeclaration (FieldDefinition field)
3385 return GetFieldName (field);
3388 protected virtual string GetEventDeclaration (EventDefinition e)
3390 return GetEventName (e);
3394 class CSharpFullMemberFormatter : MemberFormatter {
3396 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3398 string ns = DocUtils.GetNamespace (type);
3399 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
3400 buf.Append (ns).Append ('.');
3404 private string GetCSharpType (string t)
3407 case "System.Byte": return "byte";
3408 case "System.SByte": return "sbyte";
3409 case "System.Int16": return "short";
3410 case "System.Int32": return "int";
3411 case "System.Int64": return "long";
3413 case "System.UInt16": return "ushort";
3414 case "System.UInt32": return "uint";
3415 case "System.UInt64": return "ulong";
3417 case "System.Single": return "float";
3418 case "System.Double": return "double";
3419 case "System.Decimal": return "decimal";
3420 case "System.Boolean": return "bool";
3421 case "System.Char": return "char";
3422 case "System.Void": return "void";
3423 case "System.String": return "string";
3424 case "System.Object": return "object";
3429 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3431 if (type is GenericParameter)
3432 return AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
3433 string t = type.FullName;
3434 if (!t.StartsWith ("System.")) {
3435 return base.AppendTypeName (buf, type);
3438 string s = GetCSharpType (t);
3440 return buf.Append (s);
3442 return base.AppendTypeName (buf, type);
3445 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
3447 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeContainer)
3449 GenericParameterAttributes attrs = type.Attributes;
3450 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
3451 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
3455 buf.Append ("out ");
3459 protected override string GetTypeDeclaration (TypeDefinition type)
3461 string visibility = GetTypeVisibility (type.Attributes);
3462 if (visibility == null)
3465 StringBuilder buf = new StringBuilder ();
3467 buf.Append (visibility);
3470 MemberFormatter full = new CSharpFullMemberFormatter ();
3472 if (DocUtils.IsDelegate (type)) {
3473 buf.Append("delegate ");
3474 MethodDefinition invoke = type.GetMethod ("Invoke");
3475 buf.Append (full.GetName (invoke.ReturnType.ReturnType)).Append (" ");
3476 buf.Append (GetName (type));
3477 AppendParameters (buf, invoke, invoke.Parameters);
3478 AppendGenericTypeConstraints (buf, type);
3481 return buf.ToString();
3484 if (type.IsAbstract && !type.IsInterface)
3485 buf.Append("abstract ");
3486 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
3487 buf.Append("sealed ");
3488 buf.Replace ("abstract sealed", "static");
3490 buf.Append (GetTypeKind (type));
3492 buf.Append (GetCSharpType (type.FullName) == null
3497 TypeReference basetype = type.BaseType;
3498 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
3501 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
3502 .Select (iface => full.GetName (iface))
3506 if (basetype != null || interface_names.Count > 0)
3509 if (basetype != null) {
3510 buf.Append (full.GetName (basetype));
3511 if (interface_names.Count > 0)
3515 for (int i = 0; i < interface_names.Count; i++){
3518 buf.Append (interface_names [i]);
3520 AppendGenericTypeConstraints (buf, type);
3523 return buf.ToString ();
3526 static string GetTypeKind (TypeDefinition t)
3532 if (t.IsClass || t.FullName == "System.Enum")
3536 throw new ArgumentException(t.FullName);
3539 static string GetTypeVisibility (TypeAttributes ta)
3541 switch (ta & TypeAttributes.VisibilityMask) {
3542 case TypeAttributes.Public:
3543 case TypeAttributes.NestedPublic:
3546 case TypeAttributes.NestedFamily:
3547 case TypeAttributes.NestedFamORAssem:
3555 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3557 if (type.GenericParameters.Count == 0)
3559 return AppendConstraints (buf, type.GenericParameters);
3562 private StringBuilder AppendConstraints (StringBuilder buf, GenericParameterCollection genArgs)
3564 foreach (GenericParameter genArg in genArgs) {
3565 GenericParameterAttributes attrs = genArg.Attributes;
3566 ConstraintCollection constraints = genArg.Constraints;
3567 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
3570 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
3571 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
3572 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
3575 if (!isref && !isvt && !isnew && constraints.Count == 0)
3577 buf.Append (" where ").Append (genArg.Name).Append (" : ");
3579 buf.Append ("class");
3583 buf.Append ("struct");
3586 if (constraints.Count > 0 && !isvt) {
3589 buf.Append (GetTypeName (constraints [0]));
3590 for (int i = 1; i < constraints.Count; ++i)
3591 buf.Append (", ").Append (GetTypeName (constraints [i]));
3593 if (isnew && !isvt) {
3596 buf.Append ("new()");
3602 protected override string GetConstructorDeclaration (MethodDefinition constructor)
3604 StringBuilder buf = new StringBuilder ();
3605 AppendVisibility (buf, constructor);
3606 if (buf.Length == 0)
3610 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
3611 AppendParameters (buf, constructor, constructor.Parameters);
3614 return buf.ToString ();
3617 protected override string GetMethodDeclaration (MethodDefinition method)
3619 string decl = base.GetMethodDeclaration (method);
3625 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3627 if (DocUtils.IsExplicitlyImplemented (method)) {
3628 TypeReference iface;
3629 MethodReference ifaceMethod;
3630 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3631 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3633 .Append (ifaceMethod.Name);
3635 return base.AppendMethodName (buf, method);
3638 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3640 if (method.GenericParameters.Count == 0)
3642 return AppendConstraints (buf, method.GenericParameters);
3645 protected override string RefTypeModifier {
3649 protected override string GetFinalizerName (MethodDefinition method)
3651 return "~" + method.DeclaringType.Name + " ()";
3654 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3658 if (method.IsPublic)
3659 return buf.Append ("public");
3660 if (method.IsFamily || method.IsFamilyOrAssembly)
3661 return buf.Append ("protected");
3665 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3667 string modifiers = String.Empty;
3668 if (method.IsStatic) modifiers += " static";
3669 if (method.IsVirtual && !method.IsAbstract) {
3670 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3671 else modifiers += " override";
3673 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
3674 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
3675 if (method.IsFinal) modifiers += " sealed";
3676 if (modifiers == " virtual sealed") modifiers = "";
3678 return buf.Append (modifiers);
3681 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3683 if (method.IsGenericMethod ()) {
3684 GenericParameterCollection args = method.GenericParameters;
3685 if (args.Count > 0) {
3687 buf.Append (args [0].Name);
3688 for (int i = 1; i < args.Count; ++i)
3689 buf.Append (",").Append (args [i].Name);
3696 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3698 return AppendParameters (buf, method, parameters, '(', ')');
3701 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters, char begin, char end)
3705 if (parameters.Count > 0) {
3706 if (DocUtils.IsExtensionMethod (method))
3707 buf.Append ("this ");
3708 AppendParameter (buf, parameters [0]);
3709 for (int i = 1; i < parameters.Count; ++i) {
3711 AppendParameter (buf, parameters [i]);
3715 return buf.Append (end);
3718 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
3720 if (parameter.ParameterType is ReferenceType) {
3721 if (parameter.IsOut)
3722 buf.Append ("out ");
3724 buf.Append ("ref ");
3726 buf.Append (GetName (parameter.ParameterType)).Append (" ");
3727 return buf.Append (parameter.Name);
3730 protected override string GetPropertyDeclaration (PropertyDefinition property)
3732 MethodDefinition method;
3734 string get_visible = null;
3735 if ((method = property.GetMethod) != null &&
3736 (DocUtils.IsExplicitlyImplemented (method) ||
3737 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3738 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3739 string set_visible = null;
3740 if ((method = property.SetMethod) != null &&
3741 (DocUtils.IsExplicitlyImplemented (method) ||
3742 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3743 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3745 if ((set_visible == null) && (get_visible == null))
3749 StringBuilder buf = new StringBuilder ();
3750 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
3751 buf.Append (visibility = get_visible);
3752 else if (set_visible != null && get_visible == null)
3753 buf.Append (visibility = set_visible);
3755 buf.Append (visibility = "public");
3757 // Pick an accessor to use for static/virtual/override/etc. checks.
3758 method = property.SetMethod;
3760 method = property.GetMethod;
3762 string modifiers = String.Empty;
3763 if (method.IsStatic) modifiers += " static";
3764 if (method.IsVirtual && !method.IsAbstract) {
3765 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
3766 modifiers += " virtual";
3768 modifiers += " override";
3770 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
3771 if (method.IsAbstract && !declDef.IsInterface)
3772 modifiers += " abstract";
3774 modifiers += " sealed";
3775 if (modifiers == " virtual sealed")
3777 buf.Append (modifiers).Append (' ');
3779 buf.Append (GetName (property.PropertyType)).Append (' ');
3781 IEnumerable<IMemberReference> defs = property.DeclaringType.GetDefaultMembers ();
3782 string name = property.Name;
3783 foreach (IMemberReference mi in defs) {
3784 if (mi == property) {
3789 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
3791 if (property.Parameters.Count != 0) {
3792 AppendParameters (buf, method, property.Parameters, '[', ']');
3796 if (set_visible != null) {
3797 if (set_visible != visibility)
3798 buf.Append (' ').Append (set_visible);
3799 buf.Append (" set;");
3801 if (get_visible != null) {
3802 if (get_visible != visibility)
3803 buf.Append (' ').Append (get_visible);
3804 buf.Append (" get;");
3808 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
3811 protected override string GetFieldDeclaration (FieldDefinition field)
3813 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
3814 if (declType.IsEnum && field.Name == "value__")
3815 return null; // This member of enums aren't documented.
3817 StringBuilder buf = new StringBuilder ();
3818 AppendFieldVisibility (buf, field);
3819 if (buf.Length == 0)
3822 if (declType.IsEnum)
3825 if (field.IsStatic && !field.IsLiteral)
3826 buf.Append (" static");
3827 if (field.IsInitOnly)
3828 buf.Append (" readonly");
3829 if (field.IsLiteral)
3830 buf.Append (" const");
3832 buf.Append (' ').Append (GetName (field.FieldType)).Append (' ');
3833 buf.Append (field.Name);
3834 AppendFieldValue (buf, field);
3837 return buf.ToString ();
3840 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
3843 return buf.Append ("public");
3844 if (field.IsFamily || field.IsFamilyOrAssembly)
3845 return buf.Append ("protected");
3849 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
3851 // enums have a value__ field, which we ignore
3852 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
3853 field.DeclaringType.IsGenericType ())
3855 if (field.HasConstant && field.IsLiteral) {
3858 val = field.Constant;
3863 buf.Append (" = ").Append ("null");
3864 else if (val is Enum)
3865 buf.Append (" = ").Append (val.ToString ());
3866 else if (val is IFormattable) {
3867 string value = ((IFormattable)val).ToString();
3869 value = "\"" + value + "\"";
3870 buf.Append (" = ").Append (value);
3876 protected override string GetEventDeclaration (EventDefinition e)
3878 StringBuilder buf = new StringBuilder ();
3879 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
3883 AppendModifiers (buf, e.AddMethod);
3885 buf.Append (" event ");
3886 buf.Append (GetName (e.EventType)).Append (' ');
3887 buf.Append (e.Name).Append (';');
3889 return buf.ToString ();
3893 class CSharpMemberFormatter : CSharpFullMemberFormatter {
3894 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3900 class DocTypeFullMemberFormatter : MemberFormatter {
3901 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
3903 protected override char NestedTypeSeparator {
3908 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
3909 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3915 class SlashDocMemberFormatter : MemberFormatter {
3917 protected override char[] GenericTypeContainer {
3918 get {return new char[]{'{', '}'};}
3921 private bool AddTypeCount = true;
3923 private TypeReference genDeclType;
3924 private MethodReference genDeclMethod;
3926 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3928 if (type is GenericParameter) {
3930 if (genDeclType != null) {
3931 GenericParameterCollection genArgs = genDeclType.GenericParameters;
3932 for (int i = 0; i < genArgs.Count; ++i) {
3933 if (genArgs [i].Name == type.Name) {
3934 buf.Append ('`').Append (i);
3939 if (genDeclMethod != null) {
3940 GenericParameterCollection genArgs = null;
3941 if (genDeclMethod.IsGenericMethod ()) {
3942 genArgs = genDeclMethod.GenericParameters;
3943 for (int i = 0; i < genArgs.Count; ++i) {
3944 if (genArgs [i].Name == type.Name) {
3945 buf.Append ("``").Append (i);
3951 if (genDeclType == null && genDeclMethod == null) {
3952 // Probably from within an explicitly implemented interface member,
3953 // where CSC uses parameter names instead of indices (why?), e.g.
3954 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
3955 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
3956 buf.Append (type.Name);
3958 if (buf.Length == l) {
3959 throw new Exception (string.Format (
3960 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
3961 type.Name, genDeclType, genDeclMethod));
3965 base.AppendTypeName (buf, type);
3967 int numArgs = type.GenericParameters.Count;
3968 if (type.DeclaringType != null)
3969 numArgs -= type.GenericParameters.Count;
3971 buf.Append ('`').Append (numArgs);
3978 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3981 base.AppendGenericType (buf, type);
3983 AppendType (buf, type);
3987 private StringBuilder AppendType (StringBuilder buf, TypeReference type)
3989 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
3990 bool insertNested = false;
3991 int prevParamCount = 0;
3992 foreach (var decl in decls) {
3994 buf.Append (NestedTypeSeparator);
3995 insertNested = true;
3996 base.AppendTypeName (buf, decl);
3997 int argCount = DocUtils.GetGenericArgumentCount (decl);
3998 int numArgs = argCount - prevParamCount;
3999 prevParamCount = argCount;
4001 buf.Append ('`').Append (numArgs);
4006 public override string GetDeclaration (IMemberReference member)
4008 TypeReference r = member as TypeReference;
4010 return "T:" + GetTypeName (r);
4012 return base.GetDeclaration (member);
4015 protected override string GetConstructorName (MethodReference constructor)
4017 return GetMethodDefinitionName (constructor, "#ctor");
4020 protected override string GetMethodName (MethodReference method)
4023 MethodDefinition methodDef = method as MethodDefinition;
4024 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
4027 TypeReference iface;
4028 MethodReference ifaceMethod;
4029 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
4030 AddTypeCount = false;
4031 name = GetTypeName (iface) + "." + ifaceMethod.Name;
4032 AddTypeCount = true;
4034 return GetMethodDefinitionName (method, name);
4037 private string GetMethodDefinitionName (MethodReference method, string name)
4039 StringBuilder buf = new StringBuilder ();
4040 buf.Append (GetTypeName (method.DeclaringType));
4042 buf.Append (name.Replace (".", "#"));
4043 if (method.IsGenericMethod ()) {
4044 GenericParameterCollection genArgs = method.GenericParameters;
4045 if (genArgs.Count > 0)
4046 buf.Append ("``").Append (genArgs.Count);
4048 ParameterDefinitionCollection parameters = method.Parameters;
4050 genDeclType = method.DeclaringType;
4051 genDeclMethod = method;
4052 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
4056 genDeclMethod = null;
4058 return buf.ToString ();
4061 private StringBuilder AppendParameters (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinitionCollection parameters)
4063 if (parameters.Count == 0)
4068 AppendParameter (buf, genArgs, parameters [0]);
4069 for (int i = 1; i < parameters.Count; ++i) {
4071 AppendParameter (buf, genArgs, parameters [i]);
4074 return buf.Append (')');
4077 private StringBuilder AppendParameter (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinition parameter)
4079 AddTypeCount = false;
4080 buf.Append (GetTypeName (parameter.ParameterType));
4081 AddTypeCount = true;
4085 protected override string GetPropertyName (PropertyReference property)
4089 PropertyDefinition propertyDef = property as PropertyDefinition;
4090 MethodDefinition method = null;
4091 if (propertyDef != null)
4092 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
4093 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
4094 name = property.Name;
4096 TypeReference iface;
4097 MethodReference ifaceMethod;
4098 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4099 AddTypeCount = false;
4100 name = string.Join ("#", new string[]{
4101 GetTypeName (iface).Replace (".", "#"),
4102 DocUtils.GetMember (property.Name)
4104 AddTypeCount = true;
4107 StringBuilder buf = new StringBuilder ();
4108 buf.Append (GetName (property.DeclaringType));
4111 ParameterDefinitionCollection parameters = property.Parameters;
4112 if (parameters.Count > 0) {
4113 genDeclType = property.DeclaringType;
4115 GenericParameterCollection genArgs = property.DeclaringType.GenericParameters;
4116 AppendParameter (buf, genArgs, parameters [0]);
4117 for (int i = 1; i < parameters.Count; ++i) {
4119 AppendParameter (buf, genArgs, parameters [i]);
4124 return buf.ToString ();
4127 protected override string GetFieldName (FieldReference field)
4129 return string.Format ("{0}.{1}",
4130 GetName (field.DeclaringType), field.Name);
4133 protected override string GetEventName (EventReference e)
4135 return string.Format ("{0}.{1}",
4136 GetName (e.DeclaringType), e.Name);
4139 protected override string GetTypeDeclaration (TypeDefinition type)
4141 string name = GetName (type);
4147 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4149 string name = GetName (constructor);
4155 protected override string GetMethodDeclaration (MethodDefinition method)
4157 string name = GetName (method);
4160 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4161 genDeclType = method.DeclaringType;
4162 genDeclMethod = method;
4163 name += "~" + GetName (method.ReturnType.ReturnType);
4165 genDeclMethod = null;
4170 protected override string GetPropertyDeclaration (PropertyDefinition property)
4172 string name = GetName (property);
4178 protected override string GetFieldDeclaration (FieldDefinition field)
4180 string name = GetName (field);
4186 protected override string GetEventDeclaration (EventDefinition e)
4188 string name = GetName (e);
4195 class FileNameMemberFormatter : SlashDocMemberFormatter {
4196 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4201 protected override char NestedTypeSeparator {