1 // Updater program for syncing Mono's ECMA-style documentation files
3 // By Joshua Tauberer <tauberer@for.net>
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Diagnostics;
9 using System.Globalization;
14 using System.Xml.XPath;
19 using MyXmlNodeList = System.Collections.Generic.List<System.Xml.XmlNode>;
20 using StringList = System.Collections.Generic.List<string>;
21 using StringToStringMap = System.Collections.Generic.Dictionary<string, string>;
22 using StringToXmlNodeMap = System.Collections.Generic.Dictionary<string, System.Xml.XmlNode>;
24 namespace Mono.Documentation {
26 class MDocUpdater : MDocCommand
29 List<AssemblyDefinition> assemblies;
30 readonly DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver();
34 bool no_assembly_versions;
35 ExceptionLocations? exceptions;
37 internal int additions = 0, deletions = 0;
39 internal static XmlDocument slashdocs;
42 List<DocumentationImporter> importers = new List<DocumentationImporter> ();
44 DocumentationEnumerator docEnum;
48 static readonly MemberFormatter csharpFullFormatter = new CSharpFullMemberFormatter ();
49 static readonly MemberFormatter csharpFormatter = new CSharpMemberFormatter ();
50 static readonly MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
51 static readonly MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
53 internal static readonly MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
55 MyXmlNodeList extensionMethods = new MyXmlNodeList ();
57 public override void Run (IEnumerable<string> args)
59 show_exceptions = DebugOutput;
61 var types = new List<string> ();
62 var p = new OptionSet () {
64 "Delete removed members from the XML files.",
65 v => delete = v != null },
67 "Document potential exceptions that members can generate. {SOURCES} " +
68 "is a comma-separated list of:\n" +
69 " asm Method calls in same assembly\n" +
70 " depasm Method calls in dependent assemblies\n" +
71 " all Record all possible exceptions\n" +
72 "If nothing is specified, then only exceptions from the member will " +
74 v => exceptions = ParseExceptionLocations (v) },
76 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
79 case "no-assembly-versions":
80 no_assembly_versions = true;
83 throw new Exception ("Unsupported flag `" + v + "'.");
86 { "fno-assembly-versions",
87 "Do not generate //AssemblyVersion elements.",
88 v => no_assembly_versions = v != null },
90 "Import documentation from {FILE}.",
93 "Check for assembly references in {DIRECTORY}.",
94 v => assemblyResolver.AddSearchDirectory (v) },
96 "Root {DIRECTORY} to generate/update documentation.",
99 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
100 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
101 v => assemblyResolver.AddSearchDirectory (Path.GetDirectoryName (v)) },
103 "Manually specify the assembly {VERSION} that new members were added in.",
106 "Only update documentation for {TYPE}.",
107 v => types.Add (v) },
109 var assemblies = Parse (p, args, "update",
110 "[OPTIONS]+ ASSEMBLIES",
111 "Create or update documentation from ASSEMBLIES.");
112 if (assemblies == null)
114 if (assemblies.Count == 0)
115 Error ("No assemblies specified.");
117 foreach (var dir in assemblies
118 .Where (a => a.Contains (Path.DirectorySeparatorChar))
119 .Select (a => Path.GetDirectoryName (a)))
120 assemblyResolver.AddSearchDirectory (dir);
122 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
125 throw new InvalidOperationException("The --out option is required.");
127 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
129 if (import != null && ecmadocs == null && slashdocs == null) {
131 XmlReader r = new XmlTextReader (import);
133 while (r.NodeType != XmlNodeType.Element) {
135 Error ("Unable to read XML file: {0}.", import);
137 if (r.LocalName == "doc") {
138 var xml = File.ReadAllText (import);
139 // Ensure Unix line endings
140 xml = xml.Replace ("\r", "");
141 slashdocs = new XmlDocument();
142 slashdocs.LoadXml (xml);
144 else if (r.LocalName == "Libraries") {
145 ecmadocs = new XmlTextReader (import);
146 docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
147 importers.Add (new EcmaDocumentationImporter (ecmadocs));
150 Error ("Unsupported XML format within {0}.", import);
153 } catch (Exception e) {
154 Environment.ExitCode = 1;
155 Error ("Could not load XML file: {0}.", e.Message);
159 docEnum = docEnum ?? new DocumentationEnumerator ();
161 // PERFORM THE UPDATES
163 if (types.Count > 0) {
165 DoUpdateTypes (srcPath, types, srcPath);
168 else if (opts.@namespace != null)
169 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
170 Path.Combine (dest_dir, opts.@namespace));
173 DoUpdateAssemblies (srcPath, srcPath);
175 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
178 static ExceptionLocations ParseExceptionLocations (string s)
180 ExceptionLocations loc = ExceptionLocations.Member;
183 foreach (var type in s.Split (',')) {
185 case "added": loc |= ExceptionLocations.AddedMembers; break;
186 case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
187 case "asm": loc |= ExceptionLocations.Assembly; break;
188 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
189 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
195 internal void Warning (string format, params object[] args)
197 Message (TraceLevel.Warning, "mdoc: " + format, args);
200 private AssemblyDefinition LoadAssembly (string name)
202 AssemblyDefinition assembly = null;
204 assembly = AssemblyFactory.GetAssembly (name);
205 } catch (System.IO.FileNotFoundException) { }
207 if (assembly == null)
208 throw new InvalidOperationException("Assembly " + name + " not found.");
210 assembly.Resolver = assemblyResolver;
214 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
215 OrderTypeAttributes (element);
216 XmlTextWriter writer = new XmlTextWriter(output);
217 writer.Formatting = Formatting.Indented;
218 writer.Indentation = 2;
219 writer.IndentChar = ' ';
220 element.WriteTo(writer);
224 private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
226 Action<string> creator = file => {
227 using (var writer = OpenWrite (file, mode))
231 MdocFile.UpdateFile (filename, creator);
234 private static void OrderTypeAttributes (XmlElement e)
236 foreach (XmlElement type in e.SelectNodes ("//Type")) {
237 OrderTypeAttributes (type.Attributes);
241 static readonly string[] TypeAttributeOrder = {
242 "Name", "FullName", "FullNameSP", "Maintainer"
245 private static void OrderTypeAttributes (XmlAttributeCollection c)
247 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
248 for (int i = 0; i < c.Count; ++i) {
249 XmlAttribute a = c [i];
250 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
251 if (a.Name == TypeAttributeOrder [j]) {
257 for (int i = attrs.Length-1; i >= 0; --i) {
258 XmlAttribute n = attrs [i];
261 XmlAttribute r = null;
262 for (int j = i+1; j < attrs.Length; ++j) {
263 if (attrs [j] != null) {
271 c.InsertBefore (n, r);
275 private XmlDocument CreateIndexStub()
277 XmlDocument index = new XmlDocument();
279 XmlElement index_root = index.CreateElement("Overview");
280 index.AppendChild(index_root);
282 if (assemblies.Count == 0)
283 throw new Exception ("No assembly");
285 XmlElement index_assemblies = index.CreateElement("Assemblies");
286 index_root.AppendChild(index_assemblies);
288 XmlElement index_remarks = index.CreateElement("Remarks");
289 index_remarks.InnerText = "To be added.";
290 index_root.AppendChild(index_remarks);
292 XmlElement index_copyright = index.CreateElement("Copyright");
293 index_copyright.InnerText = "To be added.";
294 index_root.AppendChild(index_copyright);
296 XmlElement index_types = index.CreateElement("Types");
297 index_root.AppendChild(index_types);
302 private static void WriteNamespaceStub(string ns, string outdir) {
303 XmlDocument index = new XmlDocument();
305 XmlElement index_root = index.CreateElement("Namespace");
306 index.AppendChild(index_root);
308 index_root.SetAttribute("Name", ns);
310 XmlElement index_docs = index.CreateElement("Docs");
311 index_root.AppendChild(index_docs);
313 XmlElement index_summary = index.CreateElement("summary");
314 index_summary.InnerText = "To be added.";
315 index_docs.AppendChild(index_summary);
317 XmlElement index_remarks = index.CreateElement("remarks");
318 index_remarks.InnerText = "To be added.";
319 index_docs.AppendChild(index_remarks);
321 WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
322 writer => WriteXml (index.DocumentElement, writer));
325 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
327 var found = new HashSet<string> ();
328 foreach (AssemblyDefinition assembly in assemblies) {
329 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames)) {
330 string relpath = DoUpdateType (type, basepath, dest);
332 found.Add (type.FullName);
335 var notFound = from n in typenames where !found.Contains (n) select n;
337 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
340 public string DoUpdateType (TypeDefinition type, string basepath, string dest)
342 if (type.Namespace == null)
343 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
345 if (!IsPublic (type))
348 // Must get the A+B form of the type name.
349 string typename = GetTypeFileName(type);
351 string reltypefile = DocUtils.PathCombine (DocUtils.GetNamespace (type), typename + ".xml");
352 string typefile = Path.Combine (basepath, reltypefile);
353 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
355 string output = null;
358 } else if (dest == "-") {
361 output = Path.Combine (dest, reltypefile);
366 XmlDocument basefile = new XmlDocument();
368 basefile.Load(typefile);
369 } catch (Exception e) {
370 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
373 DoUpdateType2("Updating", basefile, type, output, false);
376 XmlElement td = StubType(type, output);
380 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
383 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
389 public void DoUpdateNS (string ns, string nspath, string outpath)
391 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
392 AssemblyDefinition assembly = assemblies [0];
394 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
395 XmlDocument basefile = new XmlDocument();
396 string typefile = Path.Combine(nspath, file.Name);
398 basefile.Load(typefile);
399 } catch (Exception e) {
400 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
404 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
405 TypeDefinition type = assembly.GetType(typename);
407 Warning ("Type no longer in assembly: " + typename);
411 seenTypes[type] = seenTypes;
412 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false);
415 // Stub types not in the directory
416 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
417 if (type.Namespace != ns || seenTypes.ContainsKey(type))
420 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"));
421 if (td == null) continue;
425 private static string GetTypeFileName (TypeReference type)
427 return filenameFormatter.GetName (type);
430 public static string GetTypeFileName (string typename)
432 StringBuilder filename = new StringBuilder (typename.Length);
436 for (int i = 0; i < typename.Length; ++i) {
437 char c = typename [i];
446 filename.Append ('`').Append ((numArgs+1).ToString());
461 return filename.ToString ();
464 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
466 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
467 index_assembly.SetAttribute ("Name", assembly.Name.Name);
468 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
470 AssemblyNameDefinition name = assembly.Name;
471 if (name.HasPublicKey) {
472 XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
473 var key = new StringBuilder (name.PublicKey.Length*3 + 2);
475 foreach (byte b in name.PublicKey)
476 key.AppendFormat ("{0,2:x2} ", b);
478 pubkey.InnerText = key.ToString ();
479 index_assembly.AppendChild (pubkey);
482 if (!string.IsNullOrEmpty (name.Culture)) {
483 XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
484 culture.InnerText = name.Culture;
485 index_assembly.AppendChild (culture);
488 MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
489 parent.AppendChild(index_assembly);
492 private void DoUpdateAssemblies (string source, string dest)
494 string indexfile = dest + "/index.xml";
496 if (System.IO.File.Exists(indexfile)) {
497 index = new XmlDocument();
498 index.Load(indexfile);
501 ClearElement(index.DocumentElement, "Assembly");
502 ClearElement(index.DocumentElement, "Attributes");
504 index = CreateIndexStub();
507 string defaultTitle = "Untitled";
508 if (assemblies.Count == 1)
509 defaultTitle = assemblies[0].Name.Name;
510 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
512 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
513 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
514 index_assemblies.RemoveAll ();
517 HashSet<string> goodfiles = new HashSet<string> ();
519 foreach (AssemblyDefinition assm in assemblies) {
520 AddIndexAssembly (assm, index_assemblies);
521 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
524 SortIndexEntries (index_types);
526 CleanupFiles (dest, goodfiles);
527 CleanupIndexTypes (index_types, goodfiles);
528 CleanupExtensions (index_types);
530 WriteFile (indexfile, FileMode.Create,
531 writer => WriteXml(index.DocumentElement, writer));
534 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
536 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
538 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
539 string typename = GetTypeFileName(type);
540 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0)
543 string reltypepath = DoUpdateType (type, source, dest);
544 if (reltypepath == null)
547 // Add namespace and type nodes into the index file as needed
548 string ns = DocUtils.GetNamespace (type);
549 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode("Namespace[@Name='" + ns + "']");
550 if (nsnode == null) {
551 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
552 nsnode.SetAttribute ("Name", ns);
553 index_types.AppendChild(nsnode);
555 string doc_typename = GetDocTypeName (type);
556 XmlElement typenode = (XmlElement)nsnode.SelectSingleNode("Type[@Name='" + typename + "']");
557 if (typenode == null) {
558 typenode = index_types.OwnerDocument.CreateElement("Type");
559 typenode.SetAttribute("Name", typename);
560 nsnode.AppendChild(typenode);
562 if (typename != doc_typename)
563 typenode.SetAttribute("DisplayName", doc_typename);
565 typenode.RemoveAttribute("DisplayName");
566 typenode.SetAttribute ("Kind", GetTypeKind (type));
568 // Ensure the namespace index file exists
569 string onsdoc = DocUtils.PathCombine (dest, type.Namespace + ".xml");
570 string nsdoc = DocUtils.PathCombine (dest, "ns-" + type.Namespace + ".xml");
571 if (File.Exists (onsdoc)) {
572 File.Move (onsdoc, nsdoc);
575 if (!File.Exists (nsdoc)) {
576 Console.WriteLine("New Namespace File: " + type.Namespace);
577 WriteNamespaceStub(type.Namespace, dest);
580 goodfiles.Add (reltypepath);
584 private static void SortIndexEntries (XmlElement indexTypes)
586 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
587 XmlNodeComparer c = new AttributeNameComparer ();
588 SortXmlNodes (indexTypes, namespaces, c);
590 for (int i = 0; i < namespaces.Count; ++i)
591 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
594 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
596 MyXmlNodeList l = new MyXmlNodeList (children.Count);
597 for (int i = 0; i < children.Count; ++i)
598 l.Add (children [i]);
600 for (int i = l.Count - 1; i > 0; --i) {
601 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
605 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
607 public abstract int Compare (XmlNode x, XmlNode y);
609 public int Compare (object x, object y)
611 return Compare ((XmlNode) x, (XmlNode) y);
615 class AttributeNameComparer : XmlNodeComparer {
618 public AttributeNameComparer ()
623 public AttributeNameComparer (string attribute)
625 this.attribute = attribute;
628 public override int Compare (XmlNode x, XmlNode y)
630 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
634 class VersionComparer : XmlNodeComparer {
635 public override int Compare (XmlNode x, XmlNode y)
637 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
638 string a = GetVersion (x.InnerText);
639 string b = GetVersion (y.InnerText);
640 return new Version (a).CompareTo (new Version (b));
643 static string GetVersion (string v)
645 int n = v.IndexOf ("x");
648 return v.Substring (0, n-1);
652 private static string GetTypeKind (TypeDefinition type)
655 return "Enumeration";
656 if (type.IsValueType)
658 if (type.IsInterface)
660 if (DocUtils.IsDelegate (type))
662 if (type.IsClass || type.FullName == "System.Enum") // FIXME
664 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
667 private static bool IsPublic (TypeDefinition type)
669 TypeDefinition decl = type;
670 while (decl != null) {
671 if (!(decl.IsPublic || decl.IsNestedPublic)) {
674 decl = (TypeDefinition) decl.DeclaringType;
679 private void CleanupFiles (string dest, HashSet<string> goodfiles)
681 // Look for files that no longer correspond to types
682 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
683 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
684 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
685 if (!goodfiles.Contains (relTypeFile)) {
686 XmlDocument doc = new XmlDocument ();
687 doc.Load (typefile.FullName);
688 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
689 if (UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
690 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
691 WriteXml(doc.DocumentElement, writer);
692 goodfiles.Add (relTypeFile);
695 string newname = typefile.FullName + ".remove";
696 try { System.IO.File.Delete(newname); } catch (Exception) { }
697 try { typefile.MoveTo(newname); } catch (Exception) { }
698 Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
704 private static TextWriter OpenWrite (string path, FileMode mode)
706 var w = new StreamWriter (
707 new FileStream (path, mode),
708 new UTF8Encoding (false)
714 private string[] GetAssemblyVersions ()
716 return (from a in assemblies select GetAssemblyVersion (a)).ToArray ();
719 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
721 // Look for type nodes that no longer correspond to types
722 MyXmlNodeList remove = new MyXmlNodeList ();
723 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
724 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
725 if (!goodfiles.Contains (fulltypename)) {
726 remove.Add (typenode);
729 foreach (XmlNode n in remove)
730 n.ParentNode.RemoveChild (n);
733 private void CleanupExtensions (XmlElement index_types)
735 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
736 if (extensionMethods.Count == 0) {
739 index_types.SelectSingleNode ("/Overview").RemoveChild (e);
743 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
744 index_types.SelectSingleNode ("/Overview").AppendChild (e);
748 extensionMethods.Sort (DefaultExtensionMethodComparer);
749 foreach (XmlNode m in extensionMethods) {
750 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
754 class ExtensionMethodComparer : XmlNodeComparer {
755 public override int Compare (XmlNode x, XmlNode y)
757 XmlNode xLink = x.SelectSingleNode ("Member/Link");
758 XmlNode yLink = y.SelectSingleNode ("Member/Link");
760 int n = xLink.Attributes ["Type"].Value.CompareTo (
761 yLink.Attributes ["Type"].Value);
764 n = xLink.Attributes ["Member"].Value.CompareTo (
765 yLink.Attributes ["Member"].Value);
766 if (n == 0 && !object.ReferenceEquals (x, y))
767 throw new InvalidOperationException ("Duplicate extension method found!");
772 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
774 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince)
776 Console.WriteLine(message + ": " + type.FullName);
778 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
780 // Update type metadata
781 UpdateType(basefile.DocumentElement, type);
783 // Update existing members. Delete member nodes that no longer should be there,
784 // and remember what members are already documented so we don't add them again.
786 MyXmlNodeList todelete = new MyXmlNodeList ();
787 foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
788 XmlElement oldmember = info.Node;
789 IMemberReference oldmember2 = info.Member;
790 string sig = oldmember2 != null ? MakeMemberSignature(oldmember2) : null;
792 // Interface implementations and overrides are deleted from the docs
793 // unless the overrides option is given.
794 if (oldmember2 != null && sig == null)
797 // Deleted (or signature changed)
798 if (oldmember2 == null) {
799 if (UpdateAssemblyVersions (oldmember, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
801 DeleteMember ("Member Removed", output, oldmember, todelete);
806 if (seenmembers.ContainsKey (sig)) {
807 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
808 // ignore, already seen
810 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
811 DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
813 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
817 // Update signature information
820 seenmembers.Add (sig, oldmember);
822 foreach (XmlElement oldmember in todelete)
823 oldmember.ParentNode.RemoveChild (oldmember);
826 if (!DocUtils.IsDelegate (type)) {
827 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
828 foreach (IMemberReference m in type.GetMembers()) {
829 if (m is TypeDefinition) continue;
831 string sig = MakeMemberSignature(m);
832 if (sig == null) continue;
833 if (seenmembers.ContainsKey(sig)) continue;
835 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
836 if (mm == null) continue;
837 members.AppendChild( mm );
839 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
844 // Import code snippets from files
845 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
846 if (!(code is XmlElement)) continue;
847 string file = ((XmlElement)code).GetAttribute("src");
848 string lang = ((XmlElement)code).GetAttribute("lang");
850 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
852 code.InnerText = src;
856 if (insertSince && since != null) {
857 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
858 docs.AppendChild (CreateSinceNode (basefile));
862 XmlElement d = basefile.DocumentElement ["Docs"];
863 XmlElement m = basefile.DocumentElement ["Members"];
864 if (d != null && m != null)
865 basefile.DocumentElement.InsertBefore (
866 basefile.DocumentElement.RemoveChild (d), m);
871 WriteXml(basefile.DocumentElement, Console.Out);
873 FileInfo file = new FileInfo (output);
874 if (!file.Directory.Exists) {
875 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
876 file.Directory.Create ();
878 WriteFile (output, FileMode.Create,
879 writer => WriteXml(basefile.DocumentElement, writer));
883 private string GetCodeSource (string lang, string file)
886 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
887 // Grab the specified region
888 string region = "#region " + file.Substring (anchorStart + 4);
889 file = file.Substring (0, anchorStart + 3);
891 using (StreamReader reader = new StreamReader (file)) {
893 StringBuilder src = new StringBuilder ();
895 while ((line = reader.ReadLine ()) != null) {
896 if (line.Trim() == region) {
897 indent = line.IndexOf (region);
900 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
905 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
908 return src.ToString ();
910 } catch (Exception e) {
911 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
912 file, region, show_exceptions ? e.ToString () : e.Message);
917 using (StreamReader reader = new StreamReader (file))
918 return reader.ReadToEnd ();
919 } catch (Exception e) {
920 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
925 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
927 string format = output != null
928 ? "{0}: File='{1}'; Signature='{4}'"
929 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
933 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
934 member.Attributes ["MemberName"].Value,
935 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
936 if (!delete && MemberDocsHaveUserContent (member)) {
937 Warning ("Member deletions must be enabled with the --delete option.");
939 todelete.Add (member);
944 class MemberComparer : XmlNodeComparer {
945 public override int Compare (XmlNode x, XmlNode y)
948 string xMemberName = x.Attributes ["MemberName"].Value;
949 string yMemberName = y.Attributes ["MemberName"].Value;
951 // generic methods *end* with '>'
952 // it's possible for explicitly implemented generic interfaces to
953 // contain <...> without being a generic method
954 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
955 (r = xMemberName.CompareTo (yMemberName)) != 0)
959 if ((lt = xMemberName.IndexOf ("<")) >= 0)
960 xMemberName = xMemberName.Substring (0, lt);
961 if ((lt = yMemberName.IndexOf ("<")) >= 0)
962 yMemberName = yMemberName.Substring (0, lt);
963 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
966 // if @MemberName matches, then it's either two different types of
967 // members sharing the same name, e.g. field & property, or it's an
968 // overloaded method.
969 // for different type, sort based on MemberType value.
970 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
971 y.SelectSingleNode ("MemberType").InnerText);
975 // same type -- must be an overloaded method. Sort based on type
976 // parameter count, then parameter count, then by the parameter
978 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
979 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
980 if (xTypeParams.Count != yTypeParams.Count)
981 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
982 for (int i = 0; i < xTypeParams.Count; ++i) {
983 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
984 yTypeParams [i].Attributes ["Name"].Value);
989 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
990 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
991 if (xParams.Count != yParams.Count)
992 return xParams.Count <= yParams.Count ? -1 : 1;
993 for (int i = 0; i < xParams.Count; ++i) {
994 r = xParams [i].Attributes ["Type"].Value.CompareTo (
995 yParams [i].Attributes ["Type"].Value);
999 // all parameters match, but return value might not match if it was
1000 // changed between one version and another.
1001 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1002 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1003 if (xReturn != null && yReturn != null) {
1004 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1013 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1015 private static void SortTypeMembers (XmlNode members)
1017 if (members == null)
1019 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1022 private static bool MemberDocsHaveUserContent (XmlNode e)
1024 e = (XmlElement)e.SelectSingleNode("Docs");
1025 if (e == null) return false;
1026 foreach (XmlElement d in e.SelectNodes("*"))
1027 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1032 // UPDATE HELPER FUNCTIONS
1034 // CREATE A STUB DOCUMENTATION FILE
1036 public XmlElement StubType (TypeDefinition type, string output)
1038 string typesig = MakeTypeSignature(type);
1039 if (typesig == null) return null; // not publicly visible
1041 XmlDocument doc = new XmlDocument();
1042 XmlElement root = doc.CreateElement("Type");
1043 doc.AppendChild (root);
1045 DoUpdateType2 ("New Type", doc, type, output, true);
1050 private XmlElement CreateSinceNode (XmlDocument doc)
1052 XmlElement s = doc.CreateElement ("since");
1053 s.SetAttribute ("version", since);
1057 // STUBBING/UPDATING FUNCTIONS
1059 public void UpdateType (XmlElement root, TypeDefinition type)
1061 root.SetAttribute("Name", GetDocTypeName (type));
1062 root.SetAttribute("FullName", GetDocTypeFullName (type));
1064 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Language", "C#");
1065 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Value", MakeTypeSignature(type));
1067 XmlElement ass = WriteElement(root, "AssemblyInfo");
1068 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1069 if (!no_assembly_versions) {
1070 UpdateAssemblyVersions (root, type, true);
1073 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1074 foreach (var version in versions)
1075 ass.RemoveChild (version);
1077 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1078 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1080 ClearElement(ass, "AssemblyCulture");
1082 // Why-oh-why do we put assembly attributes in each type file?
1083 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1084 // since they're outdated in current docs, and a waste of space.
1085 //MakeAttributes(ass, type.Assembly, true);
1086 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1087 if (assattrs != null)
1088 ass.RemoveChild(assattrs);
1090 NormalizeWhitespace(ass);
1092 if (type.IsGenericType ()) {
1093 MakeTypeParameters (root, type.GenericParameters);
1095 ClearElement(root, "TypeParameters");
1098 if (type.BaseType != null) {
1099 XmlElement basenode = WriteElement(root, "Base");
1101 string basetypename = GetDocTypeFullName (type.BaseType);
1102 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1103 WriteElementText(root, "Base/BaseTypeName", basetypename);
1105 // Document how this type instantiates the generic parameters of its base type
1106 TypeReference origBase = type.BaseType.GetOriginalType ();
1107 if (origBase.IsGenericType ()) {
1108 ClearElement(basenode, "BaseTypeArguments");
1109 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1110 GenericArgumentCollection baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1111 GenericParameterCollection baseGenParams = origBase.GenericParameters;
1112 if (baseGenArgs.Count != baseGenParams.Count)
1113 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1114 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1115 GenericParameter param = baseGenParams [i];
1116 TypeReference value = baseGenArgs [i];
1118 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1119 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1120 bta.AppendChild(arg);
1121 arg.SetAttribute ("TypeParamName", param.Name);
1122 arg.InnerText = GetDocTypeFullName (value);
1126 ClearElement(root, "Base");
1129 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1130 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1131 List<string> interface_names = userInterfaces
1132 .Select (iface => GetDocTypeFullName (iface))
1136 XmlElement interfaces = WriteElement(root, "Interfaces");
1137 interfaces.RemoveAll();
1138 foreach (string iname in interface_names) {
1139 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1140 interfaces.AppendChild(iface);
1141 WriteElementText(iface, "InterfaceName", iname);
1144 ClearElement(root, "Interfaces");
1147 MakeAttributes (root, GetCustomAttributes (type));
1149 if (DocUtils.IsDelegate (type)) {
1150 MakeTypeParameters (root, type.GenericParameters);
1151 MakeParameters(root, type.GetMethod("Invoke").Parameters);
1152 MakeReturnValue(root, type.GetMethod("Invoke"));
1155 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1156 MakeDocNode (typeInfo);
1158 if (!DocUtils.IsDelegate (type))
1159 WriteElement (root, "Members");
1161 NormalizeWhitespace(root);
1164 internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1166 List<T> l = new List<T> (list);
1171 private void UpdateMember (DocsNodeInfo info)
1173 XmlElement me = (XmlElement) info.Node;
1174 IMemberReference mi = info.Member;
1175 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Language", "C#");
1176 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Value", MakeMemberSignature(mi));
1178 WriteElementText(me, "MemberType", GetMemberType(mi));
1180 if (!no_assembly_versions) {
1181 UpdateAssemblyVersions (me, mi, true);
1184 ClearElement (me, "AssemblyInfo");
1187 MakeAttributes (me, GetCustomAttributes (mi));
1189 MakeReturnValue(me, mi);
1190 if (mi is MethodReference) {
1191 MethodReference mb = (MethodReference) mi;
1192 if (mb.IsGenericMethod ())
1193 MakeTypeParameters (me, mb.GenericParameters);
1195 MakeParameters(me, mi);
1198 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1199 WriteElementText(me, "MemberValue", fieldValue);
1201 info.Node = WriteElement (me, "Docs");
1203 UpdateExtensionMethods (me, info);
1206 IEnumerable<string> GetCustomAttributes (IMemberReference mi)
1208 IEnumerable<string> attrs = Enumerable.Empty<string>();
1210 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1212 attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1214 PropertyReference pr = mi as PropertyReference;
1216 PropertyDefinition pd = pr.Resolve ();
1217 if (pd.GetMethod != null)
1218 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1219 if (pd.SetMethod != null)
1220 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1223 EventReference er = mi as EventReference;
1225 EventDefinition ed = er.Resolve ();
1226 if (ed.AddMethod != null)
1227 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1228 if (ed.RemoveMethod != null)
1229 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1235 IEnumerable<string> GetCustomAttributes (CustomAttributeCollection attributes, string prefix)
1237 foreach (CustomAttribute attribute in attributes.Cast<CustomAttribute> ()
1238 .OrderBy (ca => ca.Constructor.DeclaringType.FullName)) {
1239 if (!attribute.Resolve ()) {
1241 Warning ("warning: could not resolve type {0}.",
1242 attribute.Constructor.DeclaringType.FullName);
1244 TypeDefinition attrType = attribute.Constructor.DeclaringType as TypeDefinition;
1245 if (attrType != null && !IsPublic (attrType))
1247 if (slashdocFormatter.GetName (attribute.Constructor.DeclaringType) == null)
1250 if (Array.IndexOf (IgnorableAttributes, attribute.Constructor.DeclaringType.FullName) >= 0)
1253 StringList fields = new StringList ();
1255 ParameterDefinitionCollection parameters = attribute.Constructor.Parameters;
1256 for (int i = 0; i < attribute.ConstructorParameters.Count; ++i) {
1257 fields.Add (MakeAttributesValueString (
1258 attribute.ConstructorParameters [i],
1259 parameters [i].ParameterType));
1262 (from de in attribute.Fields.Cast<DictionaryEntry> ()
1263 select new { Type=attribute.GetFieldType (de.Key.ToString ()), Name=de.Key, Value=de.Value })
1265 (from de in attribute.Properties.Cast<DictionaryEntry> ()
1266 select new { Type=attribute.GetPropertyType (de.Key.ToString ()), Name=de.Key, Value=de.Value }))
1267 .OrderBy (v => v.Name);
1268 foreach (var d in namedArgs)
1269 fields.Add (string.Format ("{0}={1}", d.Name,
1270 MakeAttributesValueString (d.Value, d.Type)));
1272 string a2 = String.Join(", ", fields.ToArray ());
1273 if (a2 != "") a2 = "(" + a2 + ")";
1275 string name = attribute.Constructor.DeclaringType.FullName;
1276 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1277 yield return prefix + name + a2;
1281 static readonly string[] ValidExtensionMembers = {
1290 static readonly string[] ValidExtensionDocMembers = {
1296 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1298 MethodDefinition me = info.Member as MethodDefinition;
1301 if (info.Parameters.Count < 1)
1303 if (!DocUtils.IsExtensionMethod (me))
1306 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1307 XmlNode member = e.CloneNode (true);
1308 em.AppendChild (member);
1309 RemoveExcept (member, ValidExtensionMembers);
1310 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1311 WriteElementText (member, "MemberType", "ExtensionMethod");
1312 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1313 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1314 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1315 member.AppendChild (link);
1316 AddTargets (em, info);
1318 extensionMethods.Add (em);
1321 private static void RemoveExcept (XmlNode node, string[] except)
1325 MyXmlNodeList remove = null;
1326 foreach (XmlNode n in node.ChildNodes) {
1327 if (Array.BinarySearch (except, n.Name) < 0) {
1329 remove = new MyXmlNodeList ();
1334 foreach (XmlNode n in remove)
1335 node.RemoveChild (n);
1338 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1340 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1341 member.PrependChild (targets);
1342 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1343 AppendElementAttributeText (targets, "Target", "Type",
1344 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1347 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1348 ConstraintCollection constraints = gp.Constraints;
1349 if (constraints.Count == 0)
1350 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1352 foreach (TypeReference c in constraints)
1353 AppendElementAttributeText(targets, "Target", "Type",
1354 slashdocFormatter.GetDeclaration (c));
1358 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1361 TypeDefinition type = field.DeclaringType.Resolve ();
1362 if (type != null && type.IsEnum) return false;
1364 if (type != null && type.IsGenericType ()) return false;
1365 if (!field.HasConstant)
1367 if (field.IsLiteral) {
1368 object val = field.Constant;
1369 if (val == null) value = "null";
1370 else if (val is Enum) value = val.ToString();
1371 else if (val is IFormattable) {
1372 value = ((IFormattable)val).ToString();
1374 value = "\"" + value + "\"";
1376 if (value != null && value != "")
1382 // XML HELPER FUNCTIONS
1384 internal static XmlElement WriteElement(XmlNode parent, string element) {
1385 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1387 string[] path = element.Split('/');
1388 foreach (string p in path) {
1389 ret = (XmlElement)parent.SelectSingleNode(p);
1392 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1393 ename = ename.Substring(0, ename.IndexOf('['));
1394 ret = parent.OwnerDocument.CreateElement(ename);
1395 parent.AppendChild(ret);
1404 private static void WriteElementText(XmlNode parent, string element, string value) {
1405 XmlElement node = WriteElement(parent, element);
1406 node.InnerText = value;
1409 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1411 XmlElement n = parent.OwnerDocument.CreateElement (element);
1412 parent.AppendChild (n);
1413 n.InnerText = value;
1417 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1419 XmlElement n = parent.OwnerDocument.CreateElement (element);
1420 parent.AppendChild (n);
1421 n.SetAttribute (attribute, value);
1425 private static XmlNode CopyNode (XmlNode source, XmlNode dest)
1427 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1428 dest.AppendChild (copy);
1432 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1433 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1436 node = WriteElement(parent, element);
1437 node.InnerText = value;
1439 private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
1440 XmlElement node = WriteElement(parent, element);
1441 if (node.GetAttribute(attribute) == value) return;
1442 node.SetAttribute(attribute, value);
1444 private static void ClearElement(XmlElement parent, string name) {
1445 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1447 parent.RemoveChild(node);
1450 // DOCUMENTATION HELPER FUNCTIONS
1452 private void MakeDocNode (DocsNodeInfo info)
1454 List<GenericParameter> genericParams = info.GenericParameters;
1455 ParameterDefinitionCollection parameters = info.Parameters;
1456 TypeReference returntype = info.ReturnType;
1457 bool returnisreturn = info.ReturnIsReturn;
1458 XmlElement e = info.Node;
1459 bool addremarks = info.AddRemarks;
1461 WriteElementInitialText(e, "summary", "To be added.");
1463 if (parameters != null) {
1464 string[] values = new string [parameters.Count];
1465 for (int i = 0; i < values.Length; ++i)
1466 values [i] = parameters [i].Name;
1467 UpdateParameters (e, "param", values);
1470 if (genericParams != null) {
1471 string[] values = new string [genericParams.Count];
1472 for (int i = 0; i < values.Length; ++i)
1473 values [i] = genericParams [i].Name;
1474 UpdateParameters (e, "typeparam", values);
1477 string retnodename = null;
1478 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
1479 retnodename = returnisreturn ? "returns" : "value";
1480 string retnodename_other = !returnisreturn ? "returns" : "value";
1482 // If it has a returns node instead of a value node, change its name.
1483 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1484 if (retother != null) {
1485 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1486 foreach (XmlNode node in retother)
1487 retnode.AppendChild(node.CloneNode(true));
1488 e.ReplaceChild(retnode, retother);
1490 WriteElementInitialText(e, retnodename, "To be added.");
1493 ClearElement(e, "returns");
1494 ClearElement(e, "value");
1498 WriteElementInitialText(e, "remarks", "To be added.");
1500 if (exceptions.HasValue && info.Member != null &&
1501 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
1502 UpdateExceptions (e, info.Member);
1505 foreach (DocumentationImporter importer in importers)
1506 importer.ImportDocumentation (info);
1507 if (info.SlashDocs != null) {
1508 XmlNode elem = info.SlashDocs;
1510 if (elem.SelectSingleNode("summary") != null)
1511 ClearElement(e, "summary");
1512 if (elem.SelectSingleNode("remarks") != null)
1513 ClearElement(e, "remarks");
1514 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
1515 ClearElement(e, "value");
1516 ClearElement(e, "returns");
1519 foreach (XmlNode child in elem.ChildNodes) {
1520 switch (child.Name) {
1523 XmlAttribute name = child.Attributes ["name"];
1526 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
1528 p2.InnerXml = child.InnerXml;
1531 // Occasionally XML documentation will use <returns/> on
1532 // properties, so let's try to normalize things.
1535 XmlElement v = e.OwnerDocument.CreateElement (retnodename ?? child.Name);
1536 v.InnerXml = child.InnerXml;
1542 case "permission": {
1543 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
1546 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
1548 a = e.OwnerDocument.CreateElement (child.Name);
1549 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
1552 a.InnerXml = child.InnerXml;
1556 XmlAttribute cref = child.Attributes ["cref"];
1559 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
1561 a = e.OwnerDocument.CreateElement ("altmember");
1562 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
1569 if (child.NodeType == XmlNodeType.Element &&
1570 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
1573 CopyNode (child, e);
1581 OrderDocsNodes (e, e.ChildNodes);
1582 NormalizeWhitespace(e);
1585 static readonly string[] DocsNodeOrder = {
1586 "typeparam", "param", "summary", "returns", "value", "remarks",
1589 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
1591 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1592 for (int i = 0; i < DocsNodeOrder.Length; ++i) {
1593 for (int j = 0; j < children.Count; ++j) {
1594 XmlNode c = children [j];
1595 if (c.Name == DocsNodeOrder [i]) {
1596 newChildren.Add (c);
1600 if (newChildren.Count >= 0)
1601 docs.PrependChild ((XmlNode) newChildren [0]);
1602 for (int i = 1; i < newChildren.Count; ++i) {
1603 XmlNode prev = (XmlNode) newChildren [i-1];
1604 XmlNode cur = (XmlNode) newChildren [i];
1605 docs.RemoveChild (cur);
1606 docs.InsertAfter (cur, prev);
1611 private void UpdateParameters (XmlElement e, string element, string[] values)
1613 if (values != null) {
1614 XmlNode[] paramnodes = new XmlNode[values.Length];
1616 // Some documentation had param nodes with leading spaces.
1617 foreach (XmlElement paramnode in e.SelectNodes(element)){
1618 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
1621 // If a member has only one parameter, we can track changes to
1622 // the name of the parameter easily.
1623 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
1624 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
1627 bool reinsert = false;
1629 // Pick out existing and still-valid param nodes, and
1630 // create nodes for parameters not in the file.
1631 Hashtable seenParams = new Hashtable();
1632 for (int pi = 0; pi < values.Length; pi++) {
1633 string p = values [pi];
1636 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
1637 if (paramnodes[pi] != null) continue;
1639 XmlElement pe = e.OwnerDocument.CreateElement(element);
1640 pe.SetAttribute("name", p);
1641 pe.InnerText = "To be added.";
1642 paramnodes[pi] = pe;
1646 // Remove parameters that no longer exist and check all params are in the right order.
1648 MyXmlNodeList todelete = new MyXmlNodeList ();
1649 foreach (XmlElement paramnode in e.SelectNodes(element)) {
1650 string name = paramnode.GetAttribute("name");
1651 if (!seenParams.ContainsKey(name)) {
1652 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1653 Warning ("The following param node can only be deleted if the --delete option is given: ");
1654 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
1656 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
1657 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1661 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
1662 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1663 e.ParentNode.Attributes ["MemberName"].Value,
1666 Warning ("\tValue={0}", paramnode.OuterXml);
1668 todelete.Add (paramnode);
1673 if ((int)seenParams[name] != idx)
1679 foreach (XmlNode n in todelete) {
1680 n.ParentNode.RemoveChild (n);
1683 // Re-insert the parameter nodes at the top of the doc section.
1685 for (int pi = values.Length-1; pi >= 0; pi--)
1686 e.PrependChild(paramnodes[pi]);
1688 // Clear all existing param nodes
1689 foreach (XmlNode paramnode in e.SelectNodes(element)) {
1690 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1691 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
1692 Console.WriteLine(paramnode.OuterXml);
1694 paramnode.ParentNode.RemoveChild(paramnode);
1700 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
1702 string existingName = pe.GetAttribute ("name");
1703 pe.SetAttribute ("name", newName);
1704 if (existingName == newName)
1706 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
1707 if (paramref.GetAttribute ("name").Trim () == existingName)
1708 paramref.SetAttribute ("name", newName);
1711 class CrefComparer : XmlNodeComparer {
1713 public CrefComparer ()
1717 public override int Compare (XmlNode x, XmlNode y)
1719 string xType = x.Attributes ["cref"].Value;
1720 string yType = y.Attributes ["cref"].Value;
1721 string xNamespace = GetNamespace (xType);
1722 string yNamespace = GetNamespace (yType);
1724 int c = xNamespace.CompareTo (yNamespace);
1727 return xType.CompareTo (yType);
1730 static string GetNamespace (string type)
1732 int n = type.LastIndexOf ('.');
1734 return type.Substring (0, n);
1735 return string.Empty;
1739 private void UpdateExceptions (XmlNode docs, IMemberReference member)
1741 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
1742 string cref = slashdocFormatter.GetDeclaration (source.Exception);
1743 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
1746 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
1747 e.SetAttribute ("cref", cref);
1748 e.InnerXml = "To be added; from: <see cref=\"" +
1749 string.Join ("\" />, <see cref=\"",
1750 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
1753 docs.AppendChild (e);
1755 SortXmlNodes (docs, docs.SelectNodes ("exception"),
1756 new CrefComparer ());
1759 private static void NormalizeWhitespace(XmlElement e) {
1760 // Remove all text and whitespace nodes from the element so it
1761 // is outputted with nice indentation and no blank lines.
1762 ArrayList deleteNodes = new ArrayList();
1763 foreach (XmlNode n in e)
1764 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
1766 foreach (XmlNode n in deleteNodes)
1767 n.ParentNode.RemoveChild(n);
1770 private static bool UpdateAssemblyVersions (XmlElement root, IMemberReference member, bool add)
1772 TypeDefinition type = member as TypeDefinition;
1774 type = member.DeclaringType as TypeDefinition;
1775 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
1778 private static string GetAssemblyVersion (AssemblyDefinition assembly)
1780 return assembly.Name.Version.ToString();
1783 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
1785 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
1787 e = root.OwnerDocument.CreateElement("AssemblyInfo");
1788 root.AppendChild(e);
1790 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
1791 .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
1793 // matches.Count > 0 && add: ignore -- already present
1794 if (matches.Count > 0 && !add) {
1795 foreach (XmlNode c in matches)
1798 else if (matches.Count == 0 && add) {
1799 foreach (string sv in assemblyVersions) {
1800 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
1805 // matches.Count == 0 && !add: ignore -- already not present
1807 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
1808 SortXmlNodes (e, avs, new VersionComparer ());
1810 return avs.Count != 0;
1813 // FIXME: get TypeReferences instead of string comparison?
1814 private static string[] IgnorableAttributes = {
1815 // Security related attributes
1816 "System.Reflection.AssemblyKeyFileAttribute",
1817 "System.Reflection.AssemblyDelaySignAttribute",
1818 // Present in @RefType
1819 "System.Runtime.InteropServices.OutAttribute",
1820 // For naming the indexer to use when not using indexers
1821 "System.Reflection.DefaultMemberAttribute",
1822 // for decimal constants
1823 "System.Runtime.CompilerServices.DecimalConstantAttribute",
1824 // compiler generated code
1825 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
1826 // more compiler generated code, e.g. iterator methods
1827 "System.Diagnostics.DebuggerHiddenAttribute",
1828 "System.Runtime.CompilerServices.FixedBufferAttribute",
1829 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
1830 // extension methods
1831 "System.Runtime.CompilerServices.ExtensionAttribute",
1834 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes)
1836 if (!attributes.Any ()) {
1837 ClearElement (root, "Attributes");
1841 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
1845 e = root.OwnerDocument.CreateElement("Attributes");
1847 foreach (string attribute in attributes) {
1848 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
1851 WriteElementText(ae, "AttributeName", attribute);
1854 if (e.ParentNode == null)
1855 root.AppendChild(e);
1857 NormalizeWhitespace(e);
1860 private static string MakeAttributesValueString (object v, TypeReference valueType)
1864 if (valueType.FullName == "System.Type")
1865 return "typeof(" + v.ToString () + ")";
1866 if (valueType.FullName == "System.String")
1867 return "\"" + v.ToString () + "\"";
1869 return (bool)v ? "true" : "false";
1870 TypeDefinition valueDef = valueType.Resolve ();
1871 if (valueDef == null || !valueDef.IsEnum)
1872 return v.ToString ();
1873 string typename = GetDocTypeFullName (valueType);
1874 var values = GetEnumerationValues (valueDef);
1875 long c = ToInt64 (v);
1876 if (values.ContainsKey (c))
1877 return typename + "." + values [c];
1878 if (valueDef.CustomAttributes.Cast<CustomAttribute> ()
1879 .Any (ca => ca.Constructor.DeclaringType.FullName == "System.FlagsAttribute")) {
1880 return string.Join (" | ",
1881 (from i in values.Keys
1883 select typename + "." + values [i])
1886 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
1889 private static Dictionary<long, string> GetEnumerationValues (TypeDefinition type)
1891 var values = new Dictionary<long, string> ();
1893 (from f in type.Fields.Cast<FieldDefinition> ()
1894 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
1896 values [ToInt64 (f.Constant)] = f.Name;
1901 static long ToInt64 (object value)
1904 return (long) (ulong) value;
1905 return Convert.ToInt64 (value);
1908 private void MakeParameters (XmlElement root, ParameterDefinitionCollection parameters)
1910 XmlElement e = WriteElement(root, "Parameters");
1912 foreach (ParameterDefinition p in parameters) {
1913 XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
1915 pe.SetAttribute("Name", p.Name);
1916 pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
1917 if (p.ParameterType is ReferenceType) {
1918 if (p.IsOut) pe.SetAttribute("RefType", "out");
1919 else pe.SetAttribute("RefType", "ref");
1921 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
1925 private void MakeTypeParameters (XmlElement root, GenericParameterCollection typeParams)
1927 if (typeParams == null || typeParams.Count == 0) {
1928 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
1930 root.RemoveChild (f);
1933 XmlElement e = WriteElement(root, "TypeParameters");
1935 foreach (GenericParameter t in typeParams) {
1936 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
1938 pe.SetAttribute("Name", t.Name);
1939 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""));
1940 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
1941 ConstraintCollection constraints = t.Constraints;
1942 GenericParameterAttributes attrs = t.Attributes;
1943 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
1951 ce = root.OwnerDocument.CreateElement ("Constraints");
1953 pe.AppendChild (ce);
1954 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
1955 AppendElementText (ce, "ParameterAttribute", "Contravariant");
1956 if ((attrs & GenericParameterAttributes.Covariant) != 0)
1957 AppendElementText (ce, "ParameterAttribute", "Covariant");
1958 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
1959 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
1960 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
1961 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
1962 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
1963 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
1964 foreach (TypeReference c in constraints) {
1965 TypeDefinition cd = c.Resolve ();
1966 AppendElementText (ce,
1967 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
1968 GetDocTypeFullName (c));
1973 private void MakeParameters (XmlElement root, IMemberReference mi)
1975 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
1976 MakeParameters (root, ((MethodDefinition)mi).Parameters);
1977 else if (mi is MethodDefinition) {
1978 MethodDefinition mb = (MethodDefinition) mi;
1979 ParameterDefinitionCollection parameters = mb.Parameters;
1980 MakeParameters(root, parameters);
1981 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
1982 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
1983 p.SetAttribute ("RefType", "this");
1986 else if (mi is PropertyDefinition) {
1987 ParameterDefinitionCollection parameters = ((PropertyDefinition)mi).Parameters;
1988 if (parameters.Count > 0)
1989 MakeParameters(root, parameters);
1993 else if (mi is FieldDefinition) return;
1994 else if (mi is EventDefinition) return;
1995 else throw new ArgumentException();
1998 internal static string GetDocParameterType (TypeReference type)
2000 return GetDocTypeFullName (type).Replace ("@", "&");
2003 private void MakeReturnValue (XmlElement root, TypeReference type, CustomAttributeCollection attributes)
2005 XmlElement e = WriteElement(root, "ReturnValue");
2007 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2008 if (attributes != null)
2009 MakeAttributes(e, GetCustomAttributes (attributes, ""));
2012 private void MakeReturnValue (XmlElement root, IMemberReference mi)
2014 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2016 else if (mi is MethodDefinition)
2017 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType.ReturnType, ((MethodDefinition)mi).ReturnType.CustomAttributes);
2018 else if (mi is PropertyDefinition)
2019 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
2020 else if (mi is FieldDefinition)
2021 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
2022 else if (mi is EventDefinition)
2023 MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
2025 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2028 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2030 IMemberReference mi = info.Member;
2031 if (mi is TypeDefinition) return null;
2033 string sigs = MakeMemberSignature(mi);
2034 if (sigs == null) return null; // not publicly visible
2036 // no documentation for property/event accessors. Is there a better way of doing this?
2037 if (mi.Name.StartsWith("get_")) return null;
2038 if (mi.Name.StartsWith("set_")) return null;
2039 if (mi.Name.StartsWith("add_")) return null;
2040 if (mi.Name.StartsWith("remove_")) return null;
2041 if (mi.Name.StartsWith("raise_")) return null;
2043 XmlElement me = doc.CreateElement("Member");
2044 me.SetAttribute("MemberName", GetMemberName (mi));
2048 if (exceptions.HasValue &&
2049 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2050 UpdateExceptions (info.Node, info.Member);
2052 if (since != null) {
2053 XmlNode docs = me.SelectSingleNode("Docs");
2054 docs.AppendChild (CreateSinceNode (doc));
2060 internal static string GetMemberName (IMemberReference mi)
2062 MethodDefinition mb = mi as MethodDefinition;
2064 PropertyDefinition pi = mi as PropertyDefinition;
2067 return DocUtils.GetPropertyName (pi);
2069 StringBuilder sb = new StringBuilder (mi.Name.Length);
2070 if (!DocUtils.IsExplicitlyImplemented (mb))
2071 sb.Append (mi.Name);
2073 TypeReference iface;
2074 MethodReference ifaceMethod;
2075 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2076 sb.Append (GetDocTypeFullName (iface));
2078 sb.Append (ifaceMethod.Name);
2080 if (mb.IsGenericMethod ()) {
2081 GenericParameterCollection typeParams = mb.GenericParameters;
2082 if (typeParams.Count > 0) {
2084 sb.Append (typeParams [0].Name);
2085 for (int i = 1; i < typeParams.Count; ++i)
2086 sb.Append (",").Append (typeParams [i].Name);
2090 return sb.ToString ();
2093 /// SIGNATURE GENERATION FUNCTIONS
2095 static string MakeTypeSignature (TypeReference type)
2097 return csharpFormatter.GetDeclaration (type);
2100 internal static string MakeMemberSignature (IMemberReference mi)
2102 return csharpFullFormatter.GetDeclaration (mi);
2105 internal static string GetMemberType (IMemberReference mi)
2107 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2108 return "Constructor";
2109 if (mi is MethodDefinition)
2111 if (mi is PropertyDefinition)
2113 if (mi is FieldDefinition)
2115 if (mi is EventDefinition)
2117 throw new ArgumentException();
2120 private static string GetDocTypeName (TypeReference type)
2122 return docTypeFormatter.GetName (type);
2125 internal static string GetDocTypeFullName (TypeReference type)
2127 return DocTypeFullMemberFormatter.Default.GetName (type);
2130 internal static string GetXPathForMember (DocumentationMember member)
2132 StringBuilder xpath = new StringBuilder ();
2133 xpath.Append ("//Members/Member[@MemberName=\"")
2134 .Append (member.MemberName)
2136 if (member.Parameters != null && member.Parameters.Count > 0) {
2137 xpath.Append ("/Parameters[count(Parameter) = ")
2138 .Append (member.Parameters.Count);
2139 for (int i = 0; i < member.Parameters.Count; ++i) {
2140 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2141 xpath.Append (member.Parameters [i]);
2142 xpath.Append ("\"");
2144 xpath.Append ("]/..");
2146 return xpath.ToString ();
2149 public static string GetXPathForMember (XPathNavigator member)
2151 StringBuilder xpath = new StringBuilder ();
2152 xpath.Append ("//Type[@FullName=\"")
2153 .Append (member.SelectSingleNode ("../../@FullName").Value)
2155 xpath.Append ("Members/Member[@MemberName=\"")
2156 .Append (member.SelectSingleNode ("@MemberName").Value)
2158 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2159 if (parameters.Count > 0) {
2160 xpath.Append ("/Parameters[count(Parameter) = ")
2161 .Append (parameters.Count);
2163 while (parameters.MoveNext ()) {
2165 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2166 xpath.Append (parameters.Current.Value);
2167 xpath.Append ("\"");
2169 xpath.Append ("]/..");
2171 return xpath.ToString ();
2174 public static string GetXPathForMember (IMemberReference member)
2176 StringBuilder xpath = new StringBuilder ();
2177 xpath.Append ("//Type[@FullName=\"")
2178 .Append (member.DeclaringType.FullName)
2180 xpath.Append ("Members/Member[@MemberName=\"")
2181 .Append (GetMemberName (member))
2184 ParameterDefinitionCollection parameters = null;
2185 if (member is MethodDefinition)
2186 parameters = ((MethodDefinition) member).Parameters;
2187 else if (member is PropertyDefinition) {
2188 parameters = ((PropertyDefinition) member).Parameters;
2190 if (parameters != null && parameters.Count > 0) {
2191 xpath.Append ("/Parameters[count(Parameter) = ")
2192 .Append (parameters.Count);
2193 for (int i = 0; i < parameters.Count; ++i) {
2194 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2195 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2196 xpath.Append ("\"");
2198 xpath.Append ("]/..");
2200 return xpath.ToString ();
2204 static class CecilExtensions {
2205 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type)
2207 foreach (var c in type.Constructors)
2208 yield return (IMemberReference) c;
2209 foreach (var e in type.Events)
2210 yield return (IMemberReference) e;
2211 foreach (var f in type.Fields)
2212 yield return (IMemberReference) f;
2213 foreach (var m in type.Methods)
2214 yield return (IMemberReference) m;
2215 foreach (var t in type.NestedTypes)
2216 yield return (IMemberReference) t;
2217 foreach (var p in type.Properties)
2218 yield return (IMemberReference) p;
2221 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type, string member)
2223 return GetMembers (type).Where (m => m.Name == member);
2226 public static IMemberReference GetMember (this TypeDefinition type, string member)
2228 return GetMembers (type, member).EnsureZeroOrOne ();
2231 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2233 if (source.Count () > 1)
2234 throw new InvalidOperationException ("too many matches");
2235 return source.FirstOrDefault ();
2238 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2240 return type.Methods.Cast<MethodDefinition> ()
2241 .Where (m => m.Name == method)
2242 .EnsureZeroOrOne ();
2245 public static IEnumerable<IMemberReference> GetDefaultMembers (this TypeReference type)
2247 TypeDefinition def = type as TypeDefinition;
2249 return new IMemberReference [0];
2250 CustomAttribute defMemberAttr = type.CustomAttributes.Cast<CustomAttribute> ()
2251 .Where (c => c.Constructor.DeclaringType.FullName == "System.Reflection.DefaultMemberAttribute")
2253 if (defMemberAttr == null)
2254 return new IMemberReference [0];
2255 string name = (string) defMemberAttr.ConstructorParameters [0];
2256 return def.Properties.Cast<PropertyDefinition> ()
2257 .Where (p => p.Name == name)
2258 .Select (p => (IMemberReference) p);
2261 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2263 return assembly.Modules.Cast<ModuleDefinition> ()
2264 .SelectMany (md => md.Types.Cast<TypeDefinition> ());
2267 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2269 return GetTypes (assembly)
2270 .Where (td => td.FullName == type)
2271 .EnsureZeroOrOne ();
2274 public static bool IsGenericType (this TypeReference type)
2276 return type.GenericParameters.Count > 0;
2279 public static bool IsGenericMethod (this MethodReference method)
2281 return method.GenericParameters.Count > 0;
2284 public static IMemberReference Resolve (this IMemberReference member)
2286 EventReference er = member as EventReference;
2288 return er.Resolve ();
2289 FieldReference fr = member as FieldReference;
2291 return fr.Resolve ();
2292 MethodReference mr = member as MethodReference;
2294 return mr.Resolve ();
2295 PropertyReference pr = member as PropertyReference;
2297 return pr.Resolve ();
2298 TypeReference tr = member as TypeReference;
2300 return tr.Resolve ();
2301 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2305 static class DocUtils {
2306 public static bool IsExplicitlyImplemented (MethodDefinition method)
2308 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2311 public static string GetTypeDotMember (string name)
2313 int startType, startMethod;
2314 startType = startMethod = -1;
2315 for (int i = 0; i < name.Length; ++i) {
2316 if (name [i] == '.') {
2317 startType = startMethod;
2321 return name.Substring (startType+1);
2324 public static string GetMember (string name)
2326 int i = name.LastIndexOf ('.');
2329 return name.Substring (i+1);
2332 public static void GetInfoForExplicitlyImplementedMethod (
2333 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2337 if (method.Overrides.Count != 1)
2338 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2339 iface = method.Overrides [0].DeclaringType;
2340 ifaceMethod = method.Overrides [0];
2343 public static string GetPropertyName (PropertyDefinition pi)
2345 // Issue: (g)mcs-generated assemblies that explicitly implement
2346 // properties don't specify the full namespace, just the
2347 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2348 MethodDefinition method = pi.GetMethod;
2350 method = pi.SetMethod;
2351 if (!IsExplicitlyImplemented (method))
2354 // Need to determine appropriate namespace for this member.
2355 TypeReference iface;
2356 MethodReference ifaceMethod;
2357 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2358 return string.Join (".", new string[]{
2359 DocTypeFullMemberFormatter.Default.GetName (iface),
2360 GetMember (pi.Name)});
2363 public static string GetNamespace (TypeReference type)
2365 if (type.GetOriginalType ().IsNested)
2366 type = type.GetOriginalType ();
2367 while (type != null && type.IsNested)
2368 type = type.DeclaringType;
2370 return string.Empty;
2371 return type.Namespace;
2374 public static string PathCombine (string dir, string path)
2380 return Path.Combine (dir, path);
2383 public static bool IsExtensionMethod (MethodDefinition method)
2386 method.CustomAttributes.Cast<CustomAttribute> ()
2387 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2389 method.DeclaringType.CustomAttributes.Cast<CustomAttribute> ()
2390 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2394 public static bool IsDelegate (TypeDefinition type)
2396 TypeReference baseRef = type.BaseType;
2397 if (baseRef == null)
2399 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2400 baseRef.FullName == "System.MulticastDelegate";
2403 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2405 List<TypeReference> decls = new List<TypeReference> ();
2407 while (type.DeclaringType != null) {
2408 decls.Add (type.DeclaringType);
2409 type = type.DeclaringType;
2415 public static int GetGenericArgumentCount (TypeReference type)
2417 GenericInstanceType inst = type as GenericInstanceType;
2419 ? inst.GenericArguments.Count
2420 : type.GenericParameters.Count;
2423 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
2425 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
2426 List<TypeReference> userInterfaces = new List<TypeReference> ();
2427 foreach (TypeReference iface in type.Interfaces) {
2428 TypeReference lookup = iface.Resolve () ?? iface;
2429 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
2430 userInterfaces.Add (iface);
2432 return userInterfaces;
2435 private static string GetQualifiedTypeName (TypeReference type)
2437 return "[" + type.Scope.Name + "]" + type.FullName;
2440 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
2442 HashSet<string> inheritedInterfaces = new HashSet<string> ();
2443 Action<TypeDefinition> a = null;
2445 if (t == null) return;
2446 foreach (TypeReference r in t.Interfaces) {
2447 inheritedInterfaces.Add (GetQualifiedTypeName (r));
2451 TypeReference baseRef = type.BaseType;
2452 while (baseRef != null) {
2453 TypeDefinition baseDef = baseRef.Resolve ();
2454 if (baseDef != null) {
2456 baseRef = baseDef.BaseType;
2461 foreach (TypeReference r in type.Interfaces)
2463 return inheritedInterfaces;
2467 class DocsNodeInfo {
2468 public DocsNodeInfo (XmlElement node)
2473 public DocsNodeInfo (XmlElement node, TypeDefinition type)
2479 public DocsNodeInfo (XmlElement node, IMemberReference member)
2482 SetMemberInfo (member);
2485 void SetType (TypeDefinition type)
2488 throw new ArgumentNullException ("type");
2489 GenericParameters = new List<GenericParameter> (type.GenericParameters.Cast<GenericParameter> ());
2490 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
2491 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
2492 for (int i = 0; i < declTypes.Count - 1; ++i) {
2493 int remove = System.Math.Min (maxGenArgs,
2494 DocUtils.GetGenericArgumentCount (declTypes [i]));
2495 maxGenArgs -= remove;
2496 while (remove-- > 0)
2497 GenericParameters.RemoveAt (0);
2499 if (DocUtils.IsDelegate (type)) {
2500 Parameters = type.GetMethod("Invoke").Parameters;
2501 ReturnType = type.GetMethod("Invoke").ReturnType.ReturnType;
2503 SetSlashDocs (type);
2506 void SetMemberInfo (IMemberReference member)
2509 throw new ArgumentNullException ("member");
2510 ReturnIsReturn = true;
2514 if (member is MethodReference ) {
2515 MethodReference mr = (MethodReference) member;
2516 Parameters = mr.Parameters;
2517 if (mr.IsGenericMethod ()) {
2518 GenericParameters = new List<GenericParameter> (mr.GenericParameters.Cast<GenericParameter> ());
2521 else if (member is PropertyDefinition) {
2522 Parameters = ((PropertyDefinition) member).Parameters;
2525 if (member is MethodDefinition) {
2526 ReturnType = ((MethodDefinition) member).ReturnType.ReturnType;
2527 } else if (member is PropertyDefinition) {
2528 ReturnType = ((PropertyDefinition) member).PropertyType;
2529 ReturnIsReturn = false;
2532 // no remarks section for enum members
2533 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
2535 SetSlashDocs (member);
2538 private void SetSlashDocs (IMemberReference member)
2540 if (MDocUpdater.slashdocs == null)
2543 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
2544 if (slashdocsig != null)
2545 SlashDocs = MDocUpdater.slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
2548 public TypeReference ReturnType;
2549 public List<GenericParameter> GenericParameters;
2550 public ParameterDefinitionCollection Parameters;
2551 public bool ReturnIsReturn;
2552 public XmlElement Node;
2553 public bool AddRemarks = true;
2554 public XmlNode SlashDocs;
2555 public IMemberReference Member;
2558 class DocumentationEnumerator {
2560 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2562 return GetDocumentationTypes (assembly, forTypes, null);
2565 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2567 foreach (TypeDefinition type in assembly.GetTypes()) {
2568 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
2570 if (seen != null && seen.Contains (type.FullName))
2573 foreach (TypeDefinition nested in type.NestedTypes)
2574 yield return nested;
2578 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2580 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
2581 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
2582 oldmember.RemoveAttribute ("__monodocer-seen__");
2585 IMemberReference m = GetMember (type, new DocumentationMember (oldmember));
2587 yield return new DocsNodeInfo (oldmember);
2590 yield return new DocsNodeInfo (oldmember, m);
2595 protected static IMemberReference GetMember (TypeDefinition type, DocumentationMember member)
2597 string membertype = member.MemberType;
2599 string returntype = member.ReturnType;
2601 string docName = member.MemberName;
2602 string[] docTypeParams = GetTypeParameters (docName);
2604 // Loop through all members in this type with the same name
2605 foreach (IMemberReference mi in GetReflectionMembers (type, docName)) {
2606 if (mi is TypeDefinition) continue;
2607 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
2609 string sig = MDocUpdater.MakeMemberSignature(mi);
2610 if (sig == null) continue; // not publicly visible
2612 ParameterDefinitionCollection pis = null;
2613 string[] typeParams = null;
2614 if (mi is MethodDefinition) {
2615 MethodDefinition mb = (MethodDefinition) mi;
2616 pis = mb.Parameters;
2617 if (docTypeParams != null && mb.IsGenericMethod ()) {
2618 GenericParameterCollection args = mb.GenericParameters;
2619 if (args.Count == docTypeParams.Length) {
2620 typeParams = args.Cast<GenericParameter> ().Select (p => p.Name).ToArray ();
2624 else if (mi is PropertyDefinition)
2625 pis = ((PropertyDefinition)mi).Parameters;
2627 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
2628 int pcount = pis == null ? 0 : pis.Count;
2629 if (mcount != pcount)
2632 MethodDefinition mDef = mi as MethodDefinition;
2633 if (mDef != null && !mDef.IsConstructor) {
2634 // Casting operators can overload based on return type.
2635 if (returntype != GetReplacedString (
2636 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType.ReturnType),
2637 typeParams, docTypeParams)) {
2645 for (int i = 0; i < pis.Count; i++) {
2646 string paramType = GetReplacedString (
2647 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
2648 typeParams, docTypeParams);
2649 if (paramType != (string) member.Parameters [i]) {
2654 if (!good) continue;
2662 static string[] GetTypeParameters (string docName)
2664 if (docName [docName.Length-1] != '>')
2666 StringList types = new StringList ();
2667 int endToken = docName.Length-2;
2668 int i = docName.Length-2;
2670 if (docName [i] == ',' || docName [i] == '<') {
2671 types.Add (docName.Substring (i + 1, endToken - i));
2674 if (docName [i] == '<')
2679 return types.ToArray ();
2682 protected static IEnumerable<IMemberReference> GetReflectionMembers (TypeDefinition type, string docName)
2684 // need to worry about 4 forms of //@MemberName values:
2685 // 1. "Normal" (non-generic) member names: GetEnumerator
2687 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
2688 // - try as-is, and try type.member (due to "kludge" for property
2690 // 3. "Normal" Generic member names: Sort<T> (CSC)
2691 // - need to remove generic parameters --> "Sort"
2692 // 4. Explicitly-implemented interface members for generic interfaces:
2693 // -- System.Collections.Generic.IEnumerable<T>.Current
2694 // - Try as-is, and try type.member, *keeping* the generic parameters.
2695 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
2696 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
2697 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
2698 // this as (1) or (2).
2699 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
2701 foreach (IMemberReference mi in type.GetMembers (docName))
2703 if (CountChars (docName, '.') > 0)
2704 // might be a property; try only type.member instead of
2705 // namespace.type.member.
2706 foreach (IMemberReference mi in
2707 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
2714 int startLt, startType, startMethod;
2715 startLt = startType = startMethod = -1;
2716 for (int i = 0; i < docName.Length; ++i) {
2717 switch (docName [i]) {
2726 if (numLt == 0 && (i + 1) < docName.Length)
2727 // there's another character in docName, so this <...> sequence is
2728 // probably part of a generic type -- case 4.
2732 startType = startMethod;
2738 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
2740 foreach (IMemberReference mi in type.GetMembers (refName))
2744 foreach (IMemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
2747 // If we _still_ haven't found it, we've hit another generic naming issue:
2748 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
2749 // explicitly-implemented METHOD names (not properties), e.g.
2750 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
2751 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
2752 // which the XML docs will contain.
2754 // Alas, we can't derive the Mono name from docName, so we need to iterate
2755 // over all member names, convert them into CSC format, and compare... :-(
2758 foreach (IMemberReference mi in type.GetMembers ()) {
2759 if (MDocUpdater.GetMemberName (mi) == docName)
2764 static string GetReplacedString (string typeName, string[] from, string[] to)
2768 for (int i = 0; i < from.Length; ++i)
2769 typeName = typeName.Replace (from [i], to [i]);
2773 private static int CountChars (string s, char c)
2776 for (int i = 0; i < s.Length; ++i) {
2784 class EcmaDocumentationEnumerator : DocumentationEnumerator {
2789 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
2792 this.ecmadocs = ecmaDocs;
2795 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2797 HashSet<string> seen = new HashSet<string> ();
2798 return GetDocumentationTypes (assembly, forTypes, seen)
2799 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
2802 IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2805 while (ecmadocs.Read ()) {
2806 switch (ecmadocs.Name) {
2808 if (typeDepth == -1)
2809 typeDepth = ecmadocs.Depth;
2810 if (ecmadocs.NodeType != XmlNodeType.Element)
2812 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
2814 string typename = ecmadocs.GetAttribute ("FullName");
2815 string typename2 = MDocUpdater.GetTypeFileName (typename);
2816 if (forTypes != null &&
2817 forTypes.BinarySearch (typename) < 0 &&
2818 typename != typename2 &&
2819 forTypes.BinarySearch (typename2) < 0)
2822 if ((t = assembly.GetType (typename)) == null &&
2823 (t = assembly.GetType (typename2)) == null)
2825 seen.Add (typename);
2826 if (typename != typename2)
2827 seen.Add (typename2);
2828 Console.WriteLine (" Import: {0}", t.FullName);
2829 if (ecmadocs.Name != "Docs") {
2830 int depth = ecmadocs.Depth;
2831 while (ecmadocs.Read ()) {
2832 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
2836 if (!ecmadocs.IsStartElement ("Docs"))
2837 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
2847 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2849 return GetMembers (basefile, type)
2850 .Concat (base.GetDocumentationMembers (basefile, type));
2853 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
2855 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
2858 if (ecmadocs.IsEmptyElement)
2861 int membersDepth = ecmadocs.Depth;
2863 while (go && ecmadocs.Read ()) {
2864 switch (ecmadocs.Name) {
2866 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
2868 DocumentationMember dm = new DocumentationMember (ecmadocs);
2869 string xp = MDocUpdater.GetXPathForMember (dm);
2870 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
2872 if (oldmember == null) {
2873 m = GetMember (type, dm);
2875 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2876 type.FullName, dm.MemberSignatures ["C#"]);
2877 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
2880 // oldmember lookup may have failed due to type parameter renames.
2882 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
2883 if (oldmember == null) {
2884 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
2885 oldmember = basefile.CreateElement ("Member");
2886 oldmember.SetAttribute ("MemberName", dm.MemberName);
2887 members.AppendChild (oldmember);
2888 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
2889 XmlElement ms = basefile.CreateElement ("MemberSignature");
2890 ms.SetAttribute ("Language", key);
2891 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
2892 oldmember.AppendChild (ms);
2894 oldmember.SetAttribute ("__monodocer-seen__", "true");
2895 Console.WriteLine ("Member Added: {0}", MDocUpdater.MakeMemberSignature (m));
2900 m = GetMember (type, new DocumentationMember (oldmember));
2902 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2903 type.FullName, dm.MemberSignatures ["C#"]);
2906 oldmember.SetAttribute ("__monodocer-seen__", "true");
2908 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
2909 if (ecmadocs.Name != "Docs")
2910 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
2915 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
2924 abstract class DocumentationImporter {
2926 public abstract void ImportDocumentation (DocsNodeInfo info);
2929 class EcmaDocumentationImporter : DocumentationImporter {
2933 public EcmaDocumentationImporter (XmlReader ecmaDocs)
2935 this.ecmadocs = ecmaDocs;
2938 public override void ImportDocumentation (DocsNodeInfo info)
2940 if (!ecmadocs.IsStartElement ("Docs")) {
2944 XmlElement e = info.Node;
2946 int depth = ecmadocs.Depth;
2947 ecmadocs.ReadStartElement ("Docs");
2948 while (ecmadocs.Read ()) {
2949 if (ecmadocs.Name == "Docs") {
2950 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
2953 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
2955 if (!ecmadocs.IsStartElement ())
2957 switch (ecmadocs.Name) {
2960 string name = ecmadocs.GetAttribute ("name");
2963 XmlNode doc = e.SelectSingleNode (
2964 ecmadocs.Name + "[@name='" + name + "']");
2965 string value = ecmadocs.ReadInnerXml ();
2967 doc.InnerXml = value.Replace ("\r", "");
2974 string name = ecmadocs.Name;
2975 string cref = ecmadocs.GetAttribute ("cref");
2978 XmlNode doc = e.SelectSingleNode (
2979 ecmadocs.Name + "[@cref='" + cref + "']");
2980 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
2982 doc.InnerXml = value;
2984 XmlElement n = e.OwnerDocument.CreateElement (name);
2985 n.SetAttribute ("cref", cref);
2992 string name = ecmadocs.Name;
2993 string xpath = ecmadocs.Name;
2994 StringList attributes = new StringList (ecmadocs.AttributeCount);
2995 if (ecmadocs.MoveToFirstAttribute ()) {
2997 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
2998 } while (ecmadocs.MoveToNextAttribute ());
2999 ecmadocs.MoveToContent ();
3001 if (attributes.Count > 0) {
3002 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3004 XmlNode doc = e.SelectSingleNode (xpath);
3005 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3007 doc.InnerXml = value;
3010 XmlElement n = e.OwnerDocument.CreateElement (name);
3012 foreach (string a in attributes) {
3013 int eq = a.IndexOf ('=');
3014 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3025 class DocumentationMember {
3026 public StringToStringMap MemberSignatures = new StringToStringMap ();
3027 public string ReturnType;
3028 public StringList Parameters;
3029 public string MemberName;
3030 public string MemberType;
3032 public DocumentationMember (XmlReader reader)
3034 MemberName = reader.GetAttribute ("MemberName");
3035 int depth = reader.Depth;
3037 StringList p = new StringList ();
3039 if (reader.NodeType != XmlNodeType.Element)
3041 switch (reader.Name) {
3042 case "MemberSignature":
3043 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3046 MemberType = reader.ReadElementString ();
3049 if (reader.Depth == depth + 2)
3050 ReturnType = reader.ReadElementString ();
3053 if (reader.Depth == depth + 2)
3054 p.Add (reader.GetAttribute ("Type"));
3057 if (reader.Depth == depth + 1)
3061 } while (go && reader.Read () && reader.Depth >= depth);
3067 public DocumentationMember (XmlNode node)
3069 MemberName = node.Attributes ["MemberName"].Value;
3070 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3071 XmlAttribute l = n.Attributes ["Language"];
3072 XmlAttribute v = n.Attributes ["Value"];
3073 if (l != null && v != null)
3074 MemberSignatures [l.Value] = v.Value;
3076 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3077 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
3079 ReturnType = rt.InnerText;
3080 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
3082 Parameters = new StringList (p.Count);
3083 for (int i = 0; i < p.Count; ++i)
3084 Parameters.Add (p [i].Attributes ["Type"].Value);
3089 public enum MemberFormatterState {
3092 WithinGenericTypeContainer,
3095 public abstract class MemberFormatter {
3096 public virtual string GetName (IMemberReference member)
3098 TypeReference type = member as TypeReference;
3100 return GetTypeName (type);
3101 MethodReference method = member as MethodReference;
3102 if (method != null && method.Name == ".ctor") // method.IsConstructor
3103 return GetConstructorName (method);
3105 return GetMethodName (method);
3106 PropertyReference prop = member as PropertyReference;
3108 return GetPropertyName (prop);
3109 FieldReference field = member as FieldReference;
3111 return GetFieldName (field);
3112 EventReference e = member as EventReference;
3114 return GetEventName (e);
3115 throw new NotSupportedException ("Can't handle: " +
3116 (member == null ? "null" : member.GetType().ToString()));
3119 protected virtual string GetTypeName (TypeReference type)
3122 throw new ArgumentNullException ("type");
3123 return _AppendTypeName (new StringBuilder (type.Name.Length), type).ToString ();
3126 protected virtual char[] ArrayDelimeters {
3127 get {return new char[]{'[', ']'};}
3130 protected virtual MemberFormatterState MemberFormatterState { get; set; }
3132 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type)
3134 if (type is ArrayType) {
3135 TypeSpecification spec = type as TypeSpecification;
3136 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3137 .Append (ArrayDelimeters [0]);
3138 var origState = MemberFormatterState;
3139 MemberFormatterState = MemberFormatterState.WithinArray;
3140 ArrayType array = (ArrayType) type;
3141 int rank = array.Rank;
3143 buf.Append (new string (',', rank-1));
3144 MemberFormatterState = origState;
3145 return buf.Append (ArrayDelimeters [1]);
3147 if (type is ReferenceType) {
3148 return AppendRefTypeName (buf, type);
3150 if (type is PointerType) {
3151 return AppendPointerTypeName (buf, type);
3153 AppendNamespace (buf, type);
3154 if (type is GenericParameter) {
3155 return AppendTypeName (buf, type);
3157 GenericInstanceType genInst = type as GenericInstanceType;
3158 if (type.GenericParameters.Count == 0 &&
3159 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3160 return AppendFullTypeName (buf, type);
3162 return AppendGenericType (buf, type);
3165 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3167 string ns = DocUtils.GetNamespace (type);
3168 if (ns != null && ns.Length > 0)
3169 buf.Append (ns).Append ('.');
3173 private StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type)
3175 if (type.DeclaringType != null)
3176 AppendFullTypeName (buf, type.DeclaringType).Append (NestedTypeSeparator);
3177 return AppendTypeName (buf, type);
3180 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3182 return AppendTypeName (buf, type.Name);
3185 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3187 int n = typename.IndexOf ("`");
3189 return buf.Append (typename.Substring (0, n));
3190 return buf.Append (typename);
3193 protected virtual string RefTypeModifier {
3197 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type)
3199 TypeSpecification spec = type as TypeSpecification;
3200 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3201 .Append (RefTypeModifier);
3204 protected virtual string PointerModifier {
3208 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type)
3210 TypeSpecification spec = type as TypeSpecification;
3211 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3212 .Append (PointerModifier);
3215 protected virtual char[] GenericTypeContainer {
3216 get {return new char[]{'<', '>'};}
3219 protected virtual char NestedTypeSeparator {
3223 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3225 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3226 type is GenericInstanceType ? type.GetOriginalType () : type);
3227 List<TypeReference> genArgs = GetGenericArguments (type);
3230 bool insertNested = false;
3231 foreach (var decl in decls) {
3232 TypeReference declDef = decl.Resolve () ?? decl;
3234 buf.Append (NestedTypeSeparator);
3236 insertNested = true;
3237 AppendTypeName (buf, declDef);
3238 int ac = DocUtils.GetGenericArgumentCount (declDef);
3242 buf.Append (GenericTypeContainer [0]);
3243 var origState = MemberFormatterState;
3244 MemberFormatterState = MemberFormatterState.WithinGenericTypeContainer;
3245 _AppendTypeName (buf, genArgs [argIdx++]);
3246 for (int i = 1; i < c; ++i)
3247 _AppendTypeName (buf.Append (","), genArgs [argIdx++]);
3248 MemberFormatterState = origState;
3249 buf.Append (GenericTypeContainer [1]);
3255 private List<TypeReference> GetGenericArguments (TypeReference type)
3257 var args = new List<TypeReference> ();
3258 GenericInstanceType inst = type as GenericInstanceType;
3260 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
3262 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
3266 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3271 protected virtual string GetConstructorName (MethodReference constructor)
3273 return constructor.Name;
3276 protected virtual string GetMethodName (MethodReference method)
3281 protected virtual string GetPropertyName (PropertyReference property)
3283 return property.Name;
3286 protected virtual string GetFieldName (FieldReference field)
3291 protected virtual string GetEventName (EventReference e)
3296 public virtual string GetDeclaration (IMemberReference member)
3299 throw new ArgumentNullException ("member");
3300 TypeDefinition type = member as TypeDefinition;
3302 return GetTypeDeclaration (type);
3303 MethodDefinition method = member as MethodDefinition;
3304 if (method != null && method.IsConstructor)
3305 return GetConstructorDeclaration (method);
3307 return GetMethodDeclaration (method);
3308 PropertyDefinition prop = member as PropertyDefinition;
3310 return GetPropertyDeclaration (prop);
3311 FieldDefinition field = member as FieldDefinition;
3313 return GetFieldDeclaration (field);
3314 EventDefinition e = member as EventDefinition;
3316 return GetEventDeclaration (e);
3317 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3320 protected virtual string GetTypeDeclaration (TypeDefinition type)
3323 throw new ArgumentNullException ("type");
3324 StringBuilder buf = new StringBuilder (type.Name.Length);
3325 _AppendTypeName (buf, type);
3326 AppendGenericTypeConstraints (buf, type);
3327 return buf.ToString ();
3330 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
3332 return GetConstructorName (constructor);
3335 protected virtual string GetMethodDeclaration (MethodDefinition method)
3337 // Special signature for destructors.
3338 if (method.Name == "Finalize" && method.Parameters.Count == 0)
3339 return GetFinalizerName (method);
3341 StringBuilder buf = new StringBuilder ();
3343 AppendVisibility (buf, method);
3344 if (buf.Length == 0 &&
3345 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3348 AppendModifiers (buf, method);
3350 if (buf.Length != 0)
3352 buf.Append (GetName (method.ReturnType.ReturnType)).Append (" ");
3354 AppendMethodName (buf, method);
3355 AppendGenericMethod (buf, method).Append (" ");
3356 AppendParameters (buf, method, method.Parameters);
3357 AppendGenericMethodConstraints (buf, method);
3358 return buf.ToString ();
3361 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3363 return buf.Append (method.Name);
3366 protected virtual string GetFinalizerName (MethodDefinition method)
3371 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3376 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3381 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3386 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3391 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3396 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
3398 return GetPropertyName (property);
3401 protected virtual string GetFieldDeclaration (FieldDefinition field)
3403 return GetFieldName (field);
3406 protected virtual string GetEventDeclaration (EventDefinition e)
3408 return GetEventName (e);
3412 class CSharpFullMemberFormatter : MemberFormatter {
3414 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3416 string ns = DocUtils.GetNamespace (type);
3417 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
3418 buf.Append (ns).Append ('.');
3422 private string GetCSharpType (string t)
3425 case "System.Byte": return "byte";
3426 case "System.SByte": return "sbyte";
3427 case "System.Int16": return "short";
3428 case "System.Int32": return "int";
3429 case "System.Int64": return "long";
3431 case "System.UInt16": return "ushort";
3432 case "System.UInt32": return "uint";
3433 case "System.UInt64": return "ulong";
3435 case "System.Single": return "float";
3436 case "System.Double": return "double";
3437 case "System.Decimal": return "decimal";
3438 case "System.Boolean": return "bool";
3439 case "System.Char": return "char";
3440 case "System.Void": return "void";
3441 case "System.String": return "string";
3442 case "System.Object": return "object";
3447 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3449 if (type is GenericParameter)
3450 return AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
3451 string t = type.FullName;
3452 if (!t.StartsWith ("System.")) {
3453 return base.AppendTypeName (buf, type);
3456 string s = GetCSharpType (t);
3458 return buf.Append (s);
3460 return base.AppendTypeName (buf, type);
3463 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
3465 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeContainer)
3467 GenericParameterAttributes attrs = type.Attributes;
3468 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
3469 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
3473 buf.Append ("out ");
3477 protected override string GetTypeDeclaration (TypeDefinition type)
3479 string visibility = GetTypeVisibility (type.Attributes);
3480 if (visibility == null)
3483 StringBuilder buf = new StringBuilder ();
3485 buf.Append (visibility);
3488 MemberFormatter full = new CSharpFullMemberFormatter ();
3490 if (DocUtils.IsDelegate (type)) {
3491 buf.Append("delegate ");
3492 MethodDefinition invoke = type.GetMethod ("Invoke");
3493 buf.Append (full.GetName (invoke.ReturnType.ReturnType)).Append (" ");
3494 buf.Append (GetName (type));
3495 AppendParameters (buf, invoke, invoke.Parameters);
3496 AppendGenericTypeConstraints (buf, type);
3499 return buf.ToString();
3502 if (type.IsAbstract && !type.IsInterface)
3503 buf.Append("abstract ");
3504 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
3505 buf.Append("sealed ");
3506 buf.Replace ("abstract sealed", "static");
3508 buf.Append (GetTypeKind (type));
3510 buf.Append (GetCSharpType (type.FullName) == null
3515 TypeReference basetype = type.BaseType;
3516 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
3519 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
3520 .Select (iface => full.GetName (iface))
3524 if (basetype != null || interface_names.Count > 0)
3527 if (basetype != null) {
3528 buf.Append (full.GetName (basetype));
3529 if (interface_names.Count > 0)
3533 for (int i = 0; i < interface_names.Count; i++){
3536 buf.Append (interface_names [i]);
3538 AppendGenericTypeConstraints (buf, type);
3541 return buf.ToString ();
3544 static string GetTypeKind (TypeDefinition t)
3550 if (t.IsClass || t.FullName == "System.Enum")
3554 throw new ArgumentException(t.FullName);
3557 static string GetTypeVisibility (TypeAttributes ta)
3559 switch (ta & TypeAttributes.VisibilityMask) {
3560 case TypeAttributes.Public:
3561 case TypeAttributes.NestedPublic:
3564 case TypeAttributes.NestedFamily:
3565 case TypeAttributes.NestedFamORAssem:
3573 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3575 if (type.GenericParameters.Count == 0)
3577 return AppendConstraints (buf, type.GenericParameters);
3580 private StringBuilder AppendConstraints (StringBuilder buf, GenericParameterCollection genArgs)
3582 foreach (GenericParameter genArg in genArgs) {
3583 GenericParameterAttributes attrs = genArg.Attributes;
3584 ConstraintCollection constraints = genArg.Constraints;
3585 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
3588 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
3589 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
3590 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
3593 if (!isref && !isvt && !isnew && constraints.Count == 0)
3595 buf.Append (" where ").Append (genArg.Name).Append (" : ");
3597 buf.Append ("class");
3601 buf.Append ("struct");
3604 if (constraints.Count > 0 && !isvt) {
3607 buf.Append (GetTypeName (constraints [0]));
3608 for (int i = 1; i < constraints.Count; ++i)
3609 buf.Append (", ").Append (GetTypeName (constraints [i]));
3611 if (isnew && !isvt) {
3614 buf.Append ("new()");
3620 protected override string GetConstructorDeclaration (MethodDefinition constructor)
3622 StringBuilder buf = new StringBuilder ();
3623 AppendVisibility (buf, constructor);
3624 if (buf.Length == 0)
3628 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
3629 AppendParameters (buf, constructor, constructor.Parameters);
3632 return buf.ToString ();
3635 protected override string GetMethodDeclaration (MethodDefinition method)
3637 string decl = base.GetMethodDeclaration (method);
3643 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3645 if (DocUtils.IsExplicitlyImplemented (method)) {
3646 TypeReference iface;
3647 MethodReference ifaceMethod;
3648 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3649 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3651 .Append (ifaceMethod.Name);
3653 return base.AppendMethodName (buf, method);
3656 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3658 if (method.GenericParameters.Count == 0)
3660 return AppendConstraints (buf, method.GenericParameters);
3663 protected override string RefTypeModifier {
3667 protected override string GetFinalizerName (MethodDefinition method)
3669 return "~" + method.DeclaringType.Name + " ()";
3672 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3676 if (method.IsPublic)
3677 return buf.Append ("public");
3678 if (method.IsFamily || method.IsFamilyOrAssembly)
3679 return buf.Append ("protected");
3683 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3685 string modifiers = String.Empty;
3686 if (method.IsStatic) modifiers += " static";
3687 if (method.IsVirtual && !method.IsAbstract) {
3688 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3689 else modifiers += " override";
3691 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
3692 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
3693 if (method.IsFinal) modifiers += " sealed";
3694 if (modifiers == " virtual sealed") modifiers = "";
3696 return buf.Append (modifiers);
3699 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3701 if (method.IsGenericMethod ()) {
3702 GenericParameterCollection args = method.GenericParameters;
3703 if (args.Count > 0) {
3705 buf.Append (args [0].Name);
3706 for (int i = 1; i < args.Count; ++i)
3707 buf.Append (",").Append (args [i].Name);
3714 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3716 return AppendParameters (buf, method, parameters, '(', ')');
3719 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters, char begin, char end)
3723 if (parameters.Count > 0) {
3724 if (DocUtils.IsExtensionMethod (method))
3725 buf.Append ("this ");
3726 AppendParameter (buf, parameters [0]);
3727 for (int i = 1; i < parameters.Count; ++i) {
3729 AppendParameter (buf, parameters [i]);
3733 return buf.Append (end);
3736 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
3738 if (parameter.ParameterType is ReferenceType) {
3739 if (parameter.IsOut)
3740 buf.Append ("out ");
3742 buf.Append ("ref ");
3744 buf.Append (GetName (parameter.ParameterType)).Append (" ");
3745 return buf.Append (parameter.Name);
3748 protected override string GetPropertyDeclaration (PropertyDefinition property)
3750 MethodDefinition method;
3752 string get_visible = null;
3753 if ((method = property.GetMethod) != null &&
3754 (DocUtils.IsExplicitlyImplemented (method) ||
3755 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3756 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3757 string set_visible = null;
3758 if ((method = property.SetMethod) != null &&
3759 (DocUtils.IsExplicitlyImplemented (method) ||
3760 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3761 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3763 if ((set_visible == null) && (get_visible == null))
3767 StringBuilder buf = new StringBuilder ();
3768 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
3769 buf.Append (visibility = get_visible);
3770 else if (set_visible != null && get_visible == null)
3771 buf.Append (visibility = set_visible);
3773 buf.Append (visibility = "public");
3775 // Pick an accessor to use for static/virtual/override/etc. checks.
3776 method = property.SetMethod;
3778 method = property.GetMethod;
3780 string modifiers = String.Empty;
3781 if (method.IsStatic) modifiers += " static";
3782 if (method.IsVirtual && !method.IsAbstract) {
3783 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
3784 modifiers += " virtual";
3786 modifiers += " override";
3788 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
3789 if (method.IsAbstract && !declDef.IsInterface)
3790 modifiers += " abstract";
3792 modifiers += " sealed";
3793 if (modifiers == " virtual sealed")
3795 buf.Append (modifiers).Append (' ');
3797 buf.Append (GetName (property.PropertyType)).Append (' ');
3799 IEnumerable<IMemberReference> defs = property.DeclaringType.GetDefaultMembers ();
3800 string name = property.Name;
3801 foreach (IMemberReference mi in defs) {
3802 if (mi == property) {
3807 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
3809 if (property.Parameters.Count != 0) {
3810 AppendParameters (buf, method, property.Parameters, '[', ']');
3814 if (set_visible != null) {
3815 if (set_visible != visibility)
3816 buf.Append (' ').Append (set_visible);
3817 buf.Append (" set;");
3819 if (get_visible != null) {
3820 if (get_visible != visibility)
3821 buf.Append (' ').Append (get_visible);
3822 buf.Append (" get;");
3826 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
3829 protected override string GetFieldDeclaration (FieldDefinition field)
3831 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
3832 if (declType.IsEnum && field.Name == "value__")
3833 return null; // This member of enums aren't documented.
3835 StringBuilder buf = new StringBuilder ();
3836 AppendFieldVisibility (buf, field);
3837 if (buf.Length == 0)
3840 if (declType.IsEnum)
3843 if (field.IsStatic && !field.IsLiteral)
3844 buf.Append (" static");
3845 if (field.IsInitOnly)
3846 buf.Append (" readonly");
3847 if (field.IsLiteral)
3848 buf.Append (" const");
3850 buf.Append (' ').Append (GetName (field.FieldType)).Append (' ');
3851 buf.Append (field.Name);
3852 AppendFieldValue (buf, field);
3855 return buf.ToString ();
3858 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
3861 return buf.Append ("public");
3862 if (field.IsFamily || field.IsFamilyOrAssembly)
3863 return buf.Append ("protected");
3867 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
3869 // enums have a value__ field, which we ignore
3870 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
3871 field.DeclaringType.IsGenericType ())
3873 if (field.HasConstant && field.IsLiteral) {
3876 val = field.Constant;
3881 buf.Append (" = ").Append ("null");
3882 else if (val is Enum)
3883 buf.Append (" = ").Append (val.ToString ());
3884 else if (val is IFormattable) {
3885 string value = ((IFormattable)val).ToString();
3887 value = "\"" + value + "\"";
3888 buf.Append (" = ").Append (value);
3894 protected override string GetEventDeclaration (EventDefinition e)
3896 StringBuilder buf = new StringBuilder ();
3897 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
3901 AppendModifiers (buf, e.AddMethod);
3903 buf.Append (" event ");
3904 buf.Append (GetName (e.EventType)).Append (' ');
3905 buf.Append (e.Name).Append (';');
3907 return buf.ToString ();
3911 class CSharpMemberFormatter : CSharpFullMemberFormatter {
3912 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3918 class DocTypeFullMemberFormatter : MemberFormatter {
3919 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
3921 protected override char NestedTypeSeparator {
3926 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
3927 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3933 class SlashDocMemberFormatter : MemberFormatter {
3935 protected override char[] GenericTypeContainer {
3936 get {return new char[]{'{', '}'};}
3939 private bool AddTypeCount = true;
3941 private TypeReference genDeclType;
3942 private MethodReference genDeclMethod;
3944 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3946 if (type is GenericParameter) {
3948 if (genDeclType != null) {
3949 GenericParameterCollection genArgs = genDeclType.GenericParameters;
3950 for (int i = 0; i < genArgs.Count; ++i) {
3951 if (genArgs [i].Name == type.Name) {
3952 buf.Append ('`').Append (i);
3957 if (genDeclMethod != null) {
3958 GenericParameterCollection genArgs = null;
3959 if (genDeclMethod.IsGenericMethod ()) {
3960 genArgs = genDeclMethod.GenericParameters;
3961 for (int i = 0; i < genArgs.Count; ++i) {
3962 if (genArgs [i].Name == type.Name) {
3963 buf.Append ("``").Append (i);
3969 if (genDeclType == null && genDeclMethod == null) {
3970 // Probably from within an explicitly implemented interface member,
3971 // where CSC uses parameter names instead of indices (why?), e.g.
3972 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
3973 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
3974 buf.Append (type.Name);
3976 if (buf.Length == l) {
3977 throw new Exception (string.Format (
3978 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
3979 type.Name, genDeclType, genDeclMethod));
3983 base.AppendTypeName (buf, type);
3985 int numArgs = type.GenericParameters.Count;
3986 if (type.DeclaringType != null)
3987 numArgs -= type.GenericParameters.Count;
3989 buf.Append ('`').Append (numArgs);
3996 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3999 base.AppendGenericType (buf, type);
4001 AppendType (buf, type);
4005 private StringBuilder AppendType (StringBuilder buf, TypeReference type)
4007 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
4008 bool insertNested = false;
4009 int prevParamCount = 0;
4010 foreach (var decl in decls) {
4012 buf.Append (NestedTypeSeparator);
4013 insertNested = true;
4014 base.AppendTypeName (buf, decl);
4015 int argCount = DocUtils.GetGenericArgumentCount (decl);
4016 int numArgs = argCount - prevParamCount;
4017 prevParamCount = argCount;
4019 buf.Append ('`').Append (numArgs);
4024 public override string GetDeclaration (IMemberReference member)
4026 TypeReference r = member as TypeReference;
4028 return "T:" + GetTypeName (r);
4030 return base.GetDeclaration (member);
4033 protected override string GetConstructorName (MethodReference constructor)
4035 return GetMethodDefinitionName (constructor, "#ctor");
4038 protected override string GetMethodName (MethodReference method)
4041 MethodDefinition methodDef = method as MethodDefinition;
4042 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
4045 TypeReference iface;
4046 MethodReference ifaceMethod;
4047 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
4048 AddTypeCount = false;
4049 name = GetTypeName (iface) + "." + ifaceMethod.Name;
4050 AddTypeCount = true;
4052 return GetMethodDefinitionName (method, name);
4055 private string GetMethodDefinitionName (MethodReference method, string name)
4057 StringBuilder buf = new StringBuilder ();
4058 buf.Append (GetTypeName (method.DeclaringType));
4060 buf.Append (name.Replace (".", "#"));
4061 if (method.IsGenericMethod ()) {
4062 GenericParameterCollection genArgs = method.GenericParameters;
4063 if (genArgs.Count > 0)
4064 buf.Append ("``").Append (genArgs.Count);
4066 ParameterDefinitionCollection parameters = method.Parameters;
4068 genDeclType = method.DeclaringType;
4069 genDeclMethod = method;
4070 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
4074 genDeclMethod = null;
4076 return buf.ToString ();
4079 private StringBuilder AppendParameters (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinitionCollection parameters)
4081 if (parameters.Count == 0)
4086 AppendParameter (buf, genArgs, parameters [0]);
4087 for (int i = 1; i < parameters.Count; ++i) {
4089 AppendParameter (buf, genArgs, parameters [i]);
4092 return buf.Append (')');
4095 private StringBuilder AppendParameter (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinition parameter)
4097 AddTypeCount = false;
4098 buf.Append (GetTypeName (parameter.ParameterType));
4099 AddTypeCount = true;
4103 protected override string GetPropertyName (PropertyReference property)
4107 PropertyDefinition propertyDef = property as PropertyDefinition;
4108 MethodDefinition method = null;
4109 if (propertyDef != null)
4110 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
4111 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
4112 name = property.Name;
4114 TypeReference iface;
4115 MethodReference ifaceMethod;
4116 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4117 AddTypeCount = false;
4118 name = string.Join ("#", new string[]{
4119 GetTypeName (iface).Replace (".", "#"),
4120 DocUtils.GetMember (property.Name)
4122 AddTypeCount = true;
4125 StringBuilder buf = new StringBuilder ();
4126 buf.Append (GetName (property.DeclaringType));
4129 ParameterDefinitionCollection parameters = property.Parameters;
4130 if (parameters.Count > 0) {
4131 genDeclType = property.DeclaringType;
4133 GenericParameterCollection genArgs = property.DeclaringType.GenericParameters;
4134 AppendParameter (buf, genArgs, parameters [0]);
4135 for (int i = 1; i < parameters.Count; ++i) {
4137 AppendParameter (buf, genArgs, parameters [i]);
4142 return buf.ToString ();
4145 protected override string GetFieldName (FieldReference field)
4147 return string.Format ("{0}.{1}",
4148 GetName (field.DeclaringType), field.Name);
4151 protected override string GetEventName (EventReference e)
4153 return string.Format ("{0}.{1}",
4154 GetName (e.DeclaringType), e.Name);
4157 protected override string GetTypeDeclaration (TypeDefinition type)
4159 string name = GetName (type);
4165 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4167 string name = GetName (constructor);
4173 protected override string GetMethodDeclaration (MethodDefinition method)
4175 string name = GetName (method);
4178 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4179 genDeclType = method.DeclaringType;
4180 genDeclMethod = method;
4181 name += "~" + GetName (method.ReturnType.ReturnType);
4183 genDeclMethod = null;
4188 protected override string GetPropertyDeclaration (PropertyDefinition property)
4190 string name = GetName (property);
4196 protected override string GetFieldDeclaration (FieldDefinition field)
4198 string name = GetName (field);
4204 protected override string GetEventDeclaration (EventDefinition e)
4206 string name = GetName (e);
4213 class FileNameMemberFormatter : SlashDocMemberFormatter {
4214 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4219 protected override char NestedTypeSeparator {