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, ignore_missing_types;
35 ExceptionLocations? exceptions;
37 internal int additions = 0, deletions = 0;
39 List<DocumentationImporter> importers = new List<DocumentationImporter> ();
41 DocumentationEnumerator docEnum;
45 static readonly MemberFormatter csharpFullFormatter = new CSharpFullMemberFormatter ();
46 static readonly MemberFormatter csharpFormatter = new CSharpMemberFormatter ();
47 static readonly MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
48 static readonly MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
50 internal static readonly MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
52 MyXmlNodeList extensionMethods = new MyXmlNodeList ();
54 public override void Run (IEnumerable<string> args)
56 show_exceptions = DebugOutput;
58 var types = new List<string> ();
59 var p = new OptionSet () {
61 "Delete removed members from the XML files.",
62 v => delete = v != null },
64 "Document potential exceptions that members can generate. {SOURCES} " +
65 "is a comma-separated list of:\n" +
66 " asm Method calls in same assembly\n" +
67 " depasm Method calls in dependent assemblies\n" +
68 " all Record all possible exceptions\n" +
69 " added Modifier; only create <exception/>s\n" +
70 " for NEW types/members\n" +
71 "If nothing is specified, then only exceptions from the member will " +
73 v => exceptions = ParseExceptionLocations (v) },
75 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
78 case "ignore-missing-types":
79 ignore_missing_types = true;
81 case "no-assembly-versions":
82 no_assembly_versions = true;
85 throw new Exception ("Unsupported flag `" + v + "'.");
88 { "fignore-missing-types",
89 "Do not report an error if a --type=TYPE type\nwas not found.",
90 v => ignore_missing_types = v != null },
91 { "fno-assembly-versions",
92 "Do not generate //AssemblyVersion elements.",
93 v => no_assembly_versions = v != null },
95 "Import documentation from {FILE}.",
96 v => AddImporter (v) },
98 "Check for assembly references in {DIRECTORY}.",
99 v => assemblyResolver.AddSearchDirectory (v) },
101 "Ignored for compatibility with update-ecma-xml.",
104 "Root {DIRECTORY} to generate/update documentation.",
107 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
108 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
109 v => assemblyResolver.AddSearchDirectory (Path.GetDirectoryName (v)) },
111 "Manually specify the assembly {VERSION} that new members were added in.",
114 "Only update documentation for {TYPE}.",
115 v => types.Add (v) },
117 var assemblies = Parse (p, args, "update",
118 "[OPTIONS]+ ASSEMBLIES",
119 "Create or update documentation from ASSEMBLIES.");
120 if (assemblies == null)
122 if (assemblies.Count == 0)
123 Error ("No assemblies specified.");
125 foreach (var dir in assemblies
126 .Where (a => a.Contains (Path.DirectorySeparatorChar))
127 .Select (a => Path.GetDirectoryName (a)))
128 assemblyResolver.AddSearchDirectory (dir);
130 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
133 throw new InvalidOperationException("The --out option is required.");
135 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
137 docEnum = docEnum ?? new DocumentationEnumerator ();
139 // PERFORM THE UPDATES
141 if (types.Count > 0) {
143 DoUpdateTypes (srcPath, types, srcPath);
146 else if (opts.@namespace != null)
147 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
148 Path.Combine (dest_dir, opts.@namespace));
151 DoUpdateAssemblies (srcPath, srcPath);
153 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
156 void AddImporter (string path)
159 XmlReader r = new XmlTextReader (path);
161 while (r.NodeType != XmlNodeType.Element) {
163 Error ("Unable to read XML file: {0}.", path);
165 if (r.LocalName == "doc") {
166 importers.Add (new MsxdocDocumentationImporter (path));
168 else if (r.LocalName == "Libraries") {
169 var ecmadocs = new XmlTextReader (path);
170 docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
171 importers.Add (new EcmaDocumentationImporter (ecmadocs));
174 Error ("Unsupported XML format within {0}.", path);
177 } catch (Exception e) {
178 Environment.ExitCode = 1;
179 Error ("Could not load XML file: {0}.", e.Message);
183 static ExceptionLocations ParseExceptionLocations (string s)
185 ExceptionLocations loc = ExceptionLocations.Member;
188 foreach (var type in s.Split (',')) {
190 case "added": loc |= ExceptionLocations.AddedMembers; break;
191 case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
192 case "asm": loc |= ExceptionLocations.Assembly; break;
193 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
194 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
200 internal void Warning (string format, params object[] args)
202 Message (TraceLevel.Warning, "mdoc: " + format, args);
205 private AssemblyDefinition LoadAssembly (string name)
207 AssemblyDefinition assembly = null;
209 assembly = AssemblyFactory.GetAssembly (name);
210 } catch (System.IO.FileNotFoundException) { }
212 if (assembly == null)
213 throw new InvalidOperationException("Assembly " + name + " not found.");
215 assembly.Resolver = assemblyResolver;
219 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
220 OrderTypeAttributes (element);
221 XmlTextWriter writer = new XmlTextWriter(output);
222 writer.Formatting = Formatting.Indented;
223 writer.Indentation = 2;
224 writer.IndentChar = ' ';
225 element.WriteTo(writer);
229 private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
231 Action<string> creator = file => {
232 using (var writer = OpenWrite (file, mode))
236 MdocFile.UpdateFile (filename, creator);
239 private static void OrderTypeAttributes (XmlElement e)
241 foreach (XmlElement type in e.SelectNodes ("//Type")) {
242 OrderTypeAttributes (type.Attributes);
246 static readonly string[] TypeAttributeOrder = {
247 "Name", "FullName", "FullNameSP", "Maintainer"
250 private static void OrderTypeAttributes (XmlAttributeCollection c)
252 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
253 for (int i = 0; i < c.Count; ++i) {
254 XmlAttribute a = c [i];
255 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
256 if (a.Name == TypeAttributeOrder [j]) {
262 for (int i = attrs.Length-1; i >= 0; --i) {
263 XmlAttribute n = attrs [i];
266 XmlAttribute r = null;
267 for (int j = i+1; j < attrs.Length; ++j) {
268 if (attrs [j] != null) {
276 c.InsertBefore (n, r);
280 private XmlDocument CreateIndexStub()
282 XmlDocument index = new XmlDocument();
284 XmlElement index_root = index.CreateElement("Overview");
285 index.AppendChild(index_root);
287 if (assemblies.Count == 0)
288 throw new Exception ("No assembly");
290 XmlElement index_assemblies = index.CreateElement("Assemblies");
291 index_root.AppendChild(index_assemblies);
293 XmlElement index_remarks = index.CreateElement("Remarks");
294 index_remarks.InnerText = "To be added.";
295 index_root.AppendChild(index_remarks);
297 XmlElement index_copyright = index.CreateElement("Copyright");
298 index_copyright.InnerText = "To be added.";
299 index_root.AppendChild(index_copyright);
301 XmlElement index_types = index.CreateElement("Types");
302 index_root.AppendChild(index_types);
307 private static void WriteNamespaceStub(string ns, string outdir) {
308 XmlDocument index = new XmlDocument();
310 XmlElement index_root = index.CreateElement("Namespace");
311 index.AppendChild(index_root);
313 index_root.SetAttribute("Name", ns);
315 XmlElement index_docs = index.CreateElement("Docs");
316 index_root.AppendChild(index_docs);
318 XmlElement index_summary = index.CreateElement("summary");
319 index_summary.InnerText = "To be added.";
320 index_docs.AppendChild(index_summary);
322 XmlElement index_remarks = index.CreateElement("remarks");
323 index_remarks.InnerText = "To be added.";
324 index_docs.AppendChild(index_remarks);
326 WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
327 writer => WriteXml (index.DocumentElement, writer));
330 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
332 var found = new HashSet<string> ();
333 foreach (AssemblyDefinition assembly in assemblies) {
334 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames)) {
335 string relpath = DoUpdateType (type, basepath, dest);
337 found.Add (type.FullName);
341 if (ignore_missing_types)
344 var notFound = from n in typenames where !found.Contains (n) select n;
346 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
349 public string DoUpdateType (TypeDefinition type, string basepath, string dest)
351 if (type.Namespace == null)
352 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
354 if (!IsPublic (type))
357 // Must get the A+B form of the type name.
358 string typename = GetTypeFileName(type);
360 string reltypefile = DocUtils.PathCombine (DocUtils.GetNamespace (type), typename + ".xml");
361 string typefile = Path.Combine (basepath, reltypefile);
362 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
364 string output = null;
367 } else if (dest == "-") {
370 output = Path.Combine (dest, reltypefile);
375 XmlDocument basefile = new XmlDocument();
377 basefile.Load(typefile);
378 } catch (Exception e) {
379 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
382 DoUpdateType2("Updating", basefile, type, output, false);
385 XmlElement td = StubType(type, output);
389 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
392 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
398 public void DoUpdateNS (string ns, string nspath, string outpath)
400 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
401 AssemblyDefinition assembly = assemblies [0];
403 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
404 XmlDocument basefile = new XmlDocument();
405 string typefile = Path.Combine(nspath, file.Name);
407 basefile.Load(typefile);
408 } catch (Exception e) {
409 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
413 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
414 TypeDefinition type = assembly.GetType(typename);
416 Warning ("Type no longer in assembly: " + typename);
420 seenTypes[type] = seenTypes;
421 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false);
424 // Stub types not in the directory
425 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
426 if (type.Namespace != ns || seenTypes.ContainsKey(type))
429 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"));
430 if (td == null) continue;
434 private static string GetTypeFileName (TypeReference type)
436 return filenameFormatter.GetName (type);
439 public static string GetTypeFileName (string typename)
441 StringBuilder filename = new StringBuilder (typename.Length);
445 for (int i = 0; i < typename.Length; ++i) {
446 char c = typename [i];
455 filename.Append ('`').Append ((numArgs+1).ToString());
470 return filename.ToString ();
473 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
475 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
476 index_assembly.SetAttribute ("Name", assembly.Name.Name);
477 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
479 AssemblyNameDefinition name = assembly.Name;
480 if (name.HasPublicKey) {
481 XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
482 var key = new StringBuilder (name.PublicKey.Length*3 + 2);
484 foreach (byte b in name.PublicKey)
485 key.AppendFormat ("{0,2:x2} ", b);
487 pubkey.InnerText = key.ToString ();
488 index_assembly.AppendChild (pubkey);
491 if (!string.IsNullOrEmpty (name.Culture)) {
492 XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
493 culture.InnerText = name.Culture;
494 index_assembly.AppendChild (culture);
497 MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
498 parent.AppendChild(index_assembly);
501 private void DoUpdateAssemblies (string source, string dest)
503 string indexfile = dest + "/index.xml";
505 if (System.IO.File.Exists(indexfile)) {
506 index = new XmlDocument();
507 index.Load(indexfile);
510 ClearElement(index.DocumentElement, "Assembly");
511 ClearElement(index.DocumentElement, "Attributes");
513 index = CreateIndexStub();
516 string defaultTitle = "Untitled";
517 if (assemblies.Count == 1)
518 defaultTitle = assemblies[0].Name.Name;
519 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
521 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
522 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
523 index_assemblies.RemoveAll ();
526 HashSet<string> goodfiles = new HashSet<string> ();
528 foreach (AssemblyDefinition assm in assemblies) {
529 AddIndexAssembly (assm, index_assemblies);
530 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
533 SortIndexEntries (index_types);
535 CleanupFiles (dest, goodfiles);
536 CleanupIndexTypes (index_types, goodfiles);
537 CleanupExtensions (index_types);
539 WriteFile (indexfile, FileMode.Create,
540 writer => WriteXml(index.DocumentElement, writer));
543 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
545 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
547 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
548 string typename = GetTypeFileName(type);
549 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0)
552 string reltypepath = DoUpdateType (type, source, dest);
553 if (reltypepath == null)
556 // Add namespace and type nodes into the index file as needed
557 string ns = DocUtils.GetNamespace (type);
558 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode("Namespace[@Name='" + ns + "']");
559 if (nsnode == null) {
560 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
561 nsnode.SetAttribute ("Name", ns);
562 index_types.AppendChild(nsnode);
564 string doc_typename = GetDocTypeName (type);
565 XmlElement typenode = (XmlElement)nsnode.SelectSingleNode("Type[@Name='" + typename + "']");
566 if (typenode == null) {
567 typenode = index_types.OwnerDocument.CreateElement("Type");
568 typenode.SetAttribute("Name", typename);
569 nsnode.AppendChild(typenode);
571 if (typename != doc_typename)
572 typenode.SetAttribute("DisplayName", doc_typename);
574 typenode.RemoveAttribute("DisplayName");
575 typenode.SetAttribute ("Kind", GetTypeKind (type));
577 // Ensure the namespace index file exists
578 string onsdoc = DocUtils.PathCombine (dest, type.Namespace + ".xml");
579 string nsdoc = DocUtils.PathCombine (dest, "ns-" + type.Namespace + ".xml");
580 if (File.Exists (onsdoc)) {
581 File.Move (onsdoc, nsdoc);
584 if (!File.Exists (nsdoc)) {
585 Console.WriteLine("New Namespace File: " + type.Namespace);
586 WriteNamespaceStub(type.Namespace, dest);
589 goodfiles.Add (reltypepath);
593 private static void SortIndexEntries (XmlElement indexTypes)
595 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
596 XmlNodeComparer c = new AttributeNameComparer ();
597 SortXmlNodes (indexTypes, namespaces, c);
599 for (int i = 0; i < namespaces.Count; ++i)
600 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
603 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
605 MyXmlNodeList l = new MyXmlNodeList (children.Count);
606 for (int i = 0; i < children.Count; ++i)
607 l.Add (children [i]);
609 for (int i = l.Count - 1; i > 0; --i) {
610 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
614 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
616 public abstract int Compare (XmlNode x, XmlNode y);
618 public int Compare (object x, object y)
620 return Compare ((XmlNode) x, (XmlNode) y);
624 class AttributeNameComparer : XmlNodeComparer {
627 public AttributeNameComparer ()
632 public AttributeNameComparer (string attribute)
634 this.attribute = attribute;
637 public override int Compare (XmlNode x, XmlNode y)
639 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
643 class VersionComparer : XmlNodeComparer {
644 public override int Compare (XmlNode x, XmlNode y)
646 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
647 string a = GetVersion (x.InnerText);
648 string b = GetVersion (y.InnerText);
649 return new Version (a).CompareTo (new Version (b));
652 static string GetVersion (string v)
654 int n = v.IndexOf ("x");
657 return v.Substring (0, n-1);
661 private static string GetTypeKind (TypeDefinition type)
664 return "Enumeration";
665 if (type.IsValueType)
667 if (type.IsInterface)
669 if (DocUtils.IsDelegate (type))
671 if (type.IsClass || type.FullName == "System.Enum") // FIXME
673 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
676 private static bool IsPublic (TypeDefinition type)
678 TypeDefinition decl = type;
679 while (decl != null) {
680 if (!(decl.IsPublic || decl.IsNestedPublic)) {
683 decl = (TypeDefinition) decl.DeclaringType;
688 private void CleanupFiles (string dest, HashSet<string> goodfiles)
690 // Look for files that no longer correspond to types
691 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
692 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
693 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
694 if (!goodfiles.Contains (relTypeFile)) {
695 XmlDocument doc = new XmlDocument ();
696 doc.Load (typefile.FullName);
697 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
698 if (UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
699 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
700 WriteXml(doc.DocumentElement, writer);
701 goodfiles.Add (relTypeFile);
704 string newname = typefile.FullName + ".remove";
705 try { System.IO.File.Delete(newname); } catch (Exception) { }
706 try { typefile.MoveTo(newname); } catch (Exception) { }
707 Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
713 private static TextWriter OpenWrite (string path, FileMode mode)
715 var w = new StreamWriter (
716 new FileStream (path, mode),
717 new UTF8Encoding (false)
723 private string[] GetAssemblyVersions ()
725 return (from a in assemblies select GetAssemblyVersion (a)).ToArray ();
728 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
730 // Look for type nodes that no longer correspond to types
731 MyXmlNodeList remove = new MyXmlNodeList ();
732 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
733 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
734 if (!goodfiles.Contains (fulltypename)) {
735 remove.Add (typenode);
738 foreach (XmlNode n in remove)
739 n.ParentNode.RemoveChild (n);
742 private void CleanupExtensions (XmlElement index_types)
744 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
745 if (extensionMethods.Count == 0) {
748 index_types.SelectSingleNode ("/Overview").RemoveChild (e);
752 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
753 index_types.SelectSingleNode ("/Overview").AppendChild (e);
757 extensionMethods.Sort (DefaultExtensionMethodComparer);
758 foreach (XmlNode m in extensionMethods) {
759 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
763 class ExtensionMethodComparer : XmlNodeComparer {
764 public override int Compare (XmlNode x, XmlNode y)
766 XmlNode xLink = x.SelectSingleNode ("Member/Link");
767 XmlNode yLink = y.SelectSingleNode ("Member/Link");
769 int n = xLink.Attributes ["Type"].Value.CompareTo (
770 yLink.Attributes ["Type"].Value);
773 n = xLink.Attributes ["Member"].Value.CompareTo (
774 yLink.Attributes ["Member"].Value);
775 if (n == 0 && !object.ReferenceEquals (x, y))
776 throw new InvalidOperationException ("Duplicate extension method found!");
781 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
783 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince)
785 Console.WriteLine(message + ": " + type.FullName);
787 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
789 // Update type metadata
790 UpdateType(basefile.DocumentElement, type);
792 // Update existing members. Delete member nodes that no longer should be there,
793 // and remember what members are already documented so we don't add them again.
795 MyXmlNodeList todelete = new MyXmlNodeList ();
796 foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
797 XmlElement oldmember = info.Node;
798 IMemberReference oldmember2 = info.Member;
799 string sig = oldmember2 != null ? MakeMemberSignature(oldmember2) : null;
801 // Interface implementations and overrides are deleted from the docs
802 // unless the overrides option is given.
803 if (oldmember2 != null && sig == null)
806 // Deleted (or signature changed)
807 if (oldmember2 == null) {
808 if (UpdateAssemblyVersions (oldmember, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
810 DeleteMember ("Member Removed", output, oldmember, todelete);
815 if (seenmembers.ContainsKey (sig)) {
816 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
817 // ignore, already seen
819 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
820 DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
822 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
826 // Update signature information
829 seenmembers.Add (sig, oldmember);
831 foreach (XmlElement oldmember in todelete)
832 oldmember.ParentNode.RemoveChild (oldmember);
835 if (!DocUtils.IsDelegate (type)) {
836 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
837 foreach (IMemberReference m in type.GetMembers()) {
838 if (m is TypeDefinition) continue;
840 string sig = MakeMemberSignature(m);
841 if (sig == null) continue;
842 if (seenmembers.ContainsKey(sig)) continue;
844 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
845 if (mm == null) continue;
846 members.AppendChild( mm );
848 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
853 // Import code snippets from files
854 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
855 if (!(code is XmlElement)) continue;
856 string file = ((XmlElement)code).GetAttribute("src");
857 string lang = ((XmlElement)code).GetAttribute("lang");
859 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
861 code.InnerText = src;
865 if (insertSince && since != null) {
866 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
867 docs.AppendChild (CreateSinceNode (basefile));
871 XmlElement d = basefile.DocumentElement ["Docs"];
872 XmlElement m = basefile.DocumentElement ["Members"];
873 if (d != null && m != null)
874 basefile.DocumentElement.InsertBefore (
875 basefile.DocumentElement.RemoveChild (d), m);
880 WriteXml(basefile.DocumentElement, Console.Out);
882 FileInfo file = new FileInfo (output);
883 if (!file.Directory.Exists) {
884 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
885 file.Directory.Create ();
887 WriteFile (output, FileMode.Create,
888 writer => WriteXml(basefile.DocumentElement, writer));
892 private string GetCodeSource (string lang, string file)
895 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
896 // Grab the specified region
897 string region = "#region " + file.Substring (anchorStart + 4);
898 file = file.Substring (0, anchorStart + 3);
900 using (StreamReader reader = new StreamReader (file)) {
902 StringBuilder src = new StringBuilder ();
904 while ((line = reader.ReadLine ()) != null) {
905 if (line.Trim() == region) {
906 indent = line.IndexOf (region);
909 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
914 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
917 return src.ToString ();
919 } catch (Exception e) {
920 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
921 file, region, show_exceptions ? e.ToString () : e.Message);
926 using (StreamReader reader = new StreamReader (file))
927 return reader.ReadToEnd ();
928 } catch (Exception e) {
929 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
934 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
936 string format = output != null
937 ? "{0}: File='{1}'; Signature='{4}'"
938 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
942 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
943 member.Attributes ["MemberName"].Value,
944 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
945 if (!delete && MemberDocsHaveUserContent (member)) {
946 Warning ("Member deletions must be enabled with the --delete option.");
948 todelete.Add (member);
953 class MemberComparer : XmlNodeComparer {
954 public override int Compare (XmlNode x, XmlNode y)
957 string xMemberName = x.Attributes ["MemberName"].Value;
958 string yMemberName = y.Attributes ["MemberName"].Value;
960 // generic methods *end* with '>'
961 // it's possible for explicitly implemented generic interfaces to
962 // contain <...> without being a generic method
963 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
964 (r = xMemberName.CompareTo (yMemberName)) != 0)
968 if ((lt = xMemberName.IndexOf ("<")) >= 0)
969 xMemberName = xMemberName.Substring (0, lt);
970 if ((lt = yMemberName.IndexOf ("<")) >= 0)
971 yMemberName = yMemberName.Substring (0, lt);
972 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
975 // if @MemberName matches, then it's either two different types of
976 // members sharing the same name, e.g. field & property, or it's an
977 // overloaded method.
978 // for different type, sort based on MemberType value.
979 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
980 y.SelectSingleNode ("MemberType").InnerText);
984 // same type -- must be an overloaded method. Sort based on type
985 // parameter count, then parameter count, then by the parameter
987 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
988 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
989 if (xTypeParams.Count != yTypeParams.Count)
990 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
991 for (int i = 0; i < xTypeParams.Count; ++i) {
992 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
993 yTypeParams [i].Attributes ["Name"].Value);
998 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
999 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1000 if (xParams.Count != yParams.Count)
1001 return xParams.Count <= yParams.Count ? -1 : 1;
1002 for (int i = 0; i < xParams.Count; ++i) {
1003 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1004 yParams [i].Attributes ["Type"].Value);
1008 // all parameters match, but return value might not match if it was
1009 // changed between one version and another.
1010 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1011 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1012 if (xReturn != null && yReturn != null) {
1013 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1022 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1024 private static void SortTypeMembers (XmlNode members)
1026 if (members == null)
1028 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1031 private static bool MemberDocsHaveUserContent (XmlNode e)
1033 e = (XmlElement)e.SelectSingleNode("Docs");
1034 if (e == null) return false;
1035 foreach (XmlElement d in e.SelectNodes("*"))
1036 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1041 // UPDATE HELPER FUNCTIONS
1043 // CREATE A STUB DOCUMENTATION FILE
1045 public XmlElement StubType (TypeDefinition type, string output)
1047 string typesig = MakeTypeSignature(type);
1048 if (typesig == null) return null; // not publicly visible
1050 XmlDocument doc = new XmlDocument();
1051 XmlElement root = doc.CreateElement("Type");
1052 doc.AppendChild (root);
1054 DoUpdateType2 ("New Type", doc, type, output, true);
1059 private XmlElement CreateSinceNode (XmlDocument doc)
1061 XmlElement s = doc.CreateElement ("since");
1062 s.SetAttribute ("version", since);
1066 // STUBBING/UPDATING FUNCTIONS
1068 public void UpdateType (XmlElement root, TypeDefinition type)
1070 root.SetAttribute("Name", GetDocTypeName (type));
1071 root.SetAttribute("FullName", GetDocTypeFullName (type));
1073 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Language", "C#");
1074 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Value", MakeTypeSignature(type));
1076 XmlElement ass = WriteElement(root, "AssemblyInfo");
1077 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1078 if (!no_assembly_versions) {
1079 UpdateAssemblyVersions (root, type, true);
1082 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1083 foreach (var version in versions)
1084 ass.RemoveChild (version);
1086 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1087 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1089 ClearElement(ass, "AssemblyCulture");
1091 // Why-oh-why do we put assembly attributes in each type file?
1092 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1093 // since they're outdated in current docs, and a waste of space.
1094 //MakeAttributes(ass, type.Assembly, true);
1095 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1096 if (assattrs != null)
1097 ass.RemoveChild(assattrs);
1099 NormalizeWhitespace(ass);
1101 if (type.IsGenericType ()) {
1102 MakeTypeParameters (root, type.GenericParameters);
1104 ClearElement(root, "TypeParameters");
1107 if (type.BaseType != null) {
1108 XmlElement basenode = WriteElement(root, "Base");
1110 string basetypename = GetDocTypeFullName (type.BaseType);
1111 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1112 WriteElementText(root, "Base/BaseTypeName", basetypename);
1114 // Document how this type instantiates the generic parameters of its base type
1115 TypeReference origBase = type.BaseType.GetOriginalType ();
1116 if (origBase.IsGenericType ()) {
1117 ClearElement(basenode, "BaseTypeArguments");
1118 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1119 GenericArgumentCollection baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1120 GenericParameterCollection baseGenParams = origBase.GenericParameters;
1121 if (baseGenArgs.Count != baseGenParams.Count)
1122 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1123 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1124 GenericParameter param = baseGenParams [i];
1125 TypeReference value = baseGenArgs [i];
1127 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1128 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1129 bta.AppendChild(arg);
1130 arg.SetAttribute ("TypeParamName", param.Name);
1131 arg.InnerText = GetDocTypeFullName (value);
1135 ClearElement(root, "Base");
1138 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1139 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1140 List<string> interface_names = userInterfaces
1141 .Select (iface => GetDocTypeFullName (iface))
1145 XmlElement interfaces = WriteElement(root, "Interfaces");
1146 interfaces.RemoveAll();
1147 foreach (string iname in interface_names) {
1148 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1149 interfaces.AppendChild(iface);
1150 WriteElementText(iface, "InterfaceName", iname);
1153 ClearElement(root, "Interfaces");
1156 MakeAttributes (root, GetCustomAttributes (type));
1158 if (DocUtils.IsDelegate (type)) {
1159 MakeTypeParameters (root, type.GenericParameters);
1160 MakeParameters(root, type.GetMethod("Invoke").Parameters);
1161 MakeReturnValue(root, type.GetMethod("Invoke"));
1164 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1165 MakeDocNode (typeInfo);
1167 if (!DocUtils.IsDelegate (type))
1168 WriteElement (root, "Members");
1170 NormalizeWhitespace(root);
1173 internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1175 List<T> l = new List<T> (list);
1180 private void UpdateMember (DocsNodeInfo info)
1182 XmlElement me = (XmlElement) info.Node;
1183 IMemberReference mi = info.Member;
1184 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Language", "C#");
1185 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Value", MakeMemberSignature(mi));
1187 WriteElementText(me, "MemberType", GetMemberType(mi));
1189 if (!no_assembly_versions) {
1190 UpdateAssemblyVersions (me, mi, true);
1193 ClearElement (me, "AssemblyInfo");
1196 MakeAttributes (me, GetCustomAttributes (mi));
1198 MakeReturnValue(me, mi);
1199 if (mi is MethodReference) {
1200 MethodReference mb = (MethodReference) mi;
1201 if (mb.IsGenericMethod ())
1202 MakeTypeParameters (me, mb.GenericParameters);
1204 MakeParameters(me, mi);
1207 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1208 WriteElementText(me, "MemberValue", fieldValue);
1210 info.Node = WriteElement (me, "Docs");
1212 UpdateExtensionMethods (me, info);
1215 IEnumerable<string> GetCustomAttributes (IMemberReference mi)
1217 IEnumerable<string> attrs = Enumerable.Empty<string>();
1219 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1221 attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1223 PropertyReference pr = mi as PropertyReference;
1225 PropertyDefinition pd = pr.Resolve ();
1226 if (pd.GetMethod != null)
1227 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1228 if (pd.SetMethod != null)
1229 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1232 EventReference er = mi as EventReference;
1234 EventDefinition ed = er.Resolve ();
1235 if (ed.AddMethod != null)
1236 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1237 if (ed.RemoveMethod != null)
1238 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1244 IEnumerable<string> GetCustomAttributes (CustomAttributeCollection attributes, string prefix)
1246 foreach (CustomAttribute attribute in attributes.Cast<CustomAttribute> ()
1247 .OrderBy (ca => ca.Constructor.DeclaringType.FullName)) {
1248 if (!attribute.Resolve ()) {
1250 Warning ("warning: could not resolve type {0}.",
1251 attribute.Constructor.DeclaringType.FullName);
1253 TypeDefinition attrType = attribute.Constructor.DeclaringType as TypeDefinition;
1254 if (attrType != null && !IsPublic (attrType))
1256 if (slashdocFormatter.GetName (attribute.Constructor.DeclaringType) == null)
1259 if (Array.IndexOf (IgnorableAttributes, attribute.Constructor.DeclaringType.FullName) >= 0)
1262 StringList fields = new StringList ();
1264 ParameterDefinitionCollection parameters = attribute.Constructor.Parameters;
1265 for (int i = 0; i < attribute.ConstructorParameters.Count; ++i) {
1266 fields.Add (MakeAttributesValueString (
1267 attribute.ConstructorParameters [i],
1268 parameters [i].ParameterType));
1271 (from de in attribute.Fields.Cast<DictionaryEntry> ()
1272 select new { Type=attribute.GetFieldType (de.Key.ToString ()), Name=de.Key, Value=de.Value })
1274 (from de in attribute.Properties.Cast<DictionaryEntry> ()
1275 select new { Type=attribute.GetPropertyType (de.Key.ToString ()), Name=de.Key, Value=de.Value }))
1276 .OrderBy (v => v.Name);
1277 foreach (var d in namedArgs)
1278 fields.Add (string.Format ("{0}={1}", d.Name,
1279 MakeAttributesValueString (d.Value, d.Type)));
1281 string a2 = String.Join(", ", fields.ToArray ());
1282 if (a2 != "") a2 = "(" + a2 + ")";
1284 string name = attribute.Constructor.DeclaringType.FullName;
1285 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1286 yield return prefix + name + a2;
1290 static readonly string[] ValidExtensionMembers = {
1299 static readonly string[] ValidExtensionDocMembers = {
1305 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1307 MethodDefinition me = info.Member as MethodDefinition;
1310 if (info.Parameters.Count < 1)
1312 if (!DocUtils.IsExtensionMethod (me))
1315 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1316 XmlNode member = e.CloneNode (true);
1317 em.AppendChild (member);
1318 RemoveExcept (member, ValidExtensionMembers);
1319 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1320 WriteElementText (member, "MemberType", "ExtensionMethod");
1321 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1322 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1323 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1324 member.AppendChild (link);
1325 AddTargets (em, info);
1327 extensionMethods.Add (em);
1330 private static void RemoveExcept (XmlNode node, string[] except)
1334 MyXmlNodeList remove = null;
1335 foreach (XmlNode n in node.ChildNodes) {
1336 if (Array.BinarySearch (except, n.Name) < 0) {
1338 remove = new MyXmlNodeList ();
1343 foreach (XmlNode n in remove)
1344 node.RemoveChild (n);
1347 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1349 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1350 member.PrependChild (targets);
1351 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1352 AppendElementAttributeText (targets, "Target", "Type",
1353 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1356 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1357 ConstraintCollection constraints = gp.Constraints;
1358 if (constraints.Count == 0)
1359 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1361 foreach (TypeReference c in constraints)
1362 AppendElementAttributeText(targets, "Target", "Type",
1363 slashdocFormatter.GetDeclaration (c));
1367 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1370 TypeDefinition type = field.DeclaringType.Resolve ();
1371 if (type != null && type.IsEnum) return false;
1373 if (type != null && type.IsGenericType ()) return false;
1374 if (!field.HasConstant)
1376 if (field.IsLiteral) {
1377 object val = field.Constant;
1378 if (val == null) value = "null";
1379 else if (val is Enum) value = val.ToString();
1380 else if (val is IFormattable) {
1381 value = ((IFormattable)val).ToString();
1383 value = "\"" + value + "\"";
1385 if (value != null && value != "")
1391 // XML HELPER FUNCTIONS
1393 internal static XmlElement WriteElement(XmlNode parent, string element) {
1394 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1396 string[] path = element.Split('/');
1397 foreach (string p in path) {
1398 ret = (XmlElement)parent.SelectSingleNode(p);
1401 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1402 ename = ename.Substring(0, ename.IndexOf('['));
1403 ret = parent.OwnerDocument.CreateElement(ename);
1404 parent.AppendChild(ret);
1413 private static void WriteElementText(XmlNode parent, string element, string value) {
1414 XmlElement node = WriteElement(parent, element);
1415 node.InnerText = value;
1418 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1420 XmlElement n = parent.OwnerDocument.CreateElement (element);
1421 parent.AppendChild (n);
1422 n.InnerText = value;
1426 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1428 XmlElement n = parent.OwnerDocument.CreateElement (element);
1429 parent.AppendChild (n);
1430 n.SetAttribute (attribute, value);
1434 internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
1436 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1437 dest.AppendChild (copy);
1441 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1442 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1445 node = WriteElement(parent, element);
1446 node.InnerText = value;
1448 private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
1449 XmlElement node = WriteElement(parent, element);
1450 if (node.GetAttribute(attribute) == value) return;
1451 node.SetAttribute(attribute, value);
1453 internal static void ClearElement(XmlElement parent, string name) {
1454 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1456 parent.RemoveChild(node);
1459 // DOCUMENTATION HELPER FUNCTIONS
1461 private void MakeDocNode (DocsNodeInfo info)
1463 List<GenericParameter> genericParams = info.GenericParameters;
1464 ParameterDefinitionCollection parameters = info.Parameters;
1465 TypeReference returntype = info.ReturnType;
1466 bool returnisreturn = info.ReturnIsReturn;
1467 XmlElement e = info.Node;
1468 bool addremarks = info.AddRemarks;
1470 WriteElementInitialText(e, "summary", "To be added.");
1472 if (parameters != null) {
1473 string[] values = new string [parameters.Count];
1474 for (int i = 0; i < values.Length; ++i)
1475 values [i] = parameters [i].Name;
1476 UpdateParameters (e, "param", values);
1479 if (genericParams != null) {
1480 string[] values = new string [genericParams.Count];
1481 for (int i = 0; i < values.Length; ++i)
1482 values [i] = genericParams [i].Name;
1483 UpdateParameters (e, "typeparam", values);
1486 string retnodename = null;
1487 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
1488 info.ReturnNodeName = retnodename = returnisreturn ? "returns" : "value";
1489 string retnodename_other = !returnisreturn ? "returns" : "value";
1491 // If it has a returns node instead of a value node, change its name.
1492 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1493 if (retother != null) {
1494 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1495 foreach (XmlNode node in retother)
1496 retnode.AppendChild(node.CloneNode(true));
1497 e.ReplaceChild(retnode, retother);
1499 WriteElementInitialText(e, retnodename, "To be added.");
1502 ClearElement(e, "returns");
1503 ClearElement(e, "value");
1507 WriteElementInitialText(e, "remarks", "To be added.");
1509 if (exceptions.HasValue && info.Member != null &&
1510 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
1511 UpdateExceptions (e, info.Member);
1514 foreach (DocumentationImporter importer in importers)
1515 importer.ImportDocumentation (info);
1517 OrderDocsNodes (e, e.ChildNodes);
1518 NormalizeWhitespace(e);
1521 static readonly string[] DocsNodeOrder = {
1522 "typeparam", "param", "summary", "returns", "value", "remarks",
1525 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
1527 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1528 for (int i = 0; i < DocsNodeOrder.Length; ++i) {
1529 for (int j = 0; j < children.Count; ++j) {
1530 XmlNode c = children [j];
1531 if (c.Name == DocsNodeOrder [i]) {
1532 newChildren.Add (c);
1536 if (newChildren.Count >= 0)
1537 docs.PrependChild ((XmlNode) newChildren [0]);
1538 for (int i = 1; i < newChildren.Count; ++i) {
1539 XmlNode prev = (XmlNode) newChildren [i-1];
1540 XmlNode cur = (XmlNode) newChildren [i];
1541 docs.RemoveChild (cur);
1542 docs.InsertAfter (cur, prev);
1547 private void UpdateParameters (XmlElement e, string element, string[] values)
1549 if (values != null) {
1550 XmlNode[] paramnodes = new XmlNode[values.Length];
1552 // Some documentation had param nodes with leading spaces.
1553 foreach (XmlElement paramnode in e.SelectNodes(element)){
1554 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
1557 // If a member has only one parameter, we can track changes to
1558 // the name of the parameter easily.
1559 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
1560 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
1563 bool reinsert = false;
1565 // Pick out existing and still-valid param nodes, and
1566 // create nodes for parameters not in the file.
1567 Hashtable seenParams = new Hashtable();
1568 for (int pi = 0; pi < values.Length; pi++) {
1569 string p = values [pi];
1572 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
1573 if (paramnodes[pi] != null) continue;
1575 XmlElement pe = e.OwnerDocument.CreateElement(element);
1576 pe.SetAttribute("name", p);
1577 pe.InnerText = "To be added.";
1578 paramnodes[pi] = pe;
1582 // Remove parameters that no longer exist and check all params are in the right order.
1584 MyXmlNodeList todelete = new MyXmlNodeList ();
1585 foreach (XmlElement paramnode in e.SelectNodes(element)) {
1586 string name = paramnode.GetAttribute("name");
1587 if (!seenParams.ContainsKey(name)) {
1588 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1589 Warning ("The following param node can only be deleted if the --delete option is given: ");
1590 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
1592 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
1593 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1597 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
1598 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1599 e.ParentNode.Attributes ["MemberName"].Value,
1602 Warning ("\tValue={0}", paramnode.OuterXml);
1604 todelete.Add (paramnode);
1609 if ((int)seenParams[name] != idx)
1615 foreach (XmlNode n in todelete) {
1616 n.ParentNode.RemoveChild (n);
1619 // Re-insert the parameter nodes at the top of the doc section.
1621 for (int pi = values.Length-1; pi >= 0; pi--)
1622 e.PrependChild(paramnodes[pi]);
1624 // Clear all existing param nodes
1625 foreach (XmlNode paramnode in e.SelectNodes(element)) {
1626 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1627 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
1628 Console.WriteLine(paramnode.OuterXml);
1630 paramnode.ParentNode.RemoveChild(paramnode);
1636 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
1638 string existingName = pe.GetAttribute ("name");
1639 pe.SetAttribute ("name", newName);
1640 if (existingName == newName)
1642 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
1643 if (paramref.GetAttribute ("name").Trim () == existingName)
1644 paramref.SetAttribute ("name", newName);
1647 class CrefComparer : XmlNodeComparer {
1649 public CrefComparer ()
1653 public override int Compare (XmlNode x, XmlNode y)
1655 string xType = x.Attributes ["cref"].Value;
1656 string yType = y.Attributes ["cref"].Value;
1657 string xNamespace = GetNamespace (xType);
1658 string yNamespace = GetNamespace (yType);
1660 int c = xNamespace.CompareTo (yNamespace);
1663 return xType.CompareTo (yType);
1666 static string GetNamespace (string type)
1668 int n = type.LastIndexOf ('.');
1670 return type.Substring (0, n);
1671 return string.Empty;
1675 private void UpdateExceptions (XmlNode docs, IMemberReference member)
1677 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
1678 string cref = slashdocFormatter.GetDeclaration (source.Exception);
1679 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
1682 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
1683 e.SetAttribute ("cref", cref);
1684 e.InnerXml = "To be added; from: <see cref=\"" +
1685 string.Join ("\" />, <see cref=\"",
1686 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
1689 docs.AppendChild (e);
1691 SortXmlNodes (docs, docs.SelectNodes ("exception"),
1692 new CrefComparer ());
1695 private static void NormalizeWhitespace(XmlElement e) {
1696 // Remove all text and whitespace nodes from the element so it
1697 // is outputted with nice indentation and no blank lines.
1698 ArrayList deleteNodes = new ArrayList();
1699 foreach (XmlNode n in e)
1700 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
1702 foreach (XmlNode n in deleteNodes)
1703 n.ParentNode.RemoveChild(n);
1706 private static bool UpdateAssemblyVersions (XmlElement root, IMemberReference member, bool add)
1708 TypeDefinition type = member as TypeDefinition;
1710 type = member.DeclaringType as TypeDefinition;
1711 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
1714 private static string GetAssemblyVersion (AssemblyDefinition assembly)
1716 return assembly.Name.Version.ToString();
1719 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
1721 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
1723 e = root.OwnerDocument.CreateElement("AssemblyInfo");
1724 root.AppendChild(e);
1726 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
1727 .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
1729 // matches.Count > 0 && add: ignore -- already present
1730 if (matches.Count > 0 && !add) {
1731 foreach (XmlNode c in matches)
1734 else if (matches.Count == 0 && add) {
1735 foreach (string sv in assemblyVersions) {
1736 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
1741 // matches.Count == 0 && !add: ignore -- already not present
1743 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
1744 SortXmlNodes (e, avs, new VersionComparer ());
1746 return avs.Count != 0;
1749 // FIXME: get TypeReferences instead of string comparison?
1750 private static string[] IgnorableAttributes = {
1751 // Security related attributes
1752 "System.Reflection.AssemblyKeyFileAttribute",
1753 "System.Reflection.AssemblyDelaySignAttribute",
1754 // Present in @RefType
1755 "System.Runtime.InteropServices.OutAttribute",
1756 // For naming the indexer to use when not using indexers
1757 "System.Reflection.DefaultMemberAttribute",
1758 // for decimal constants
1759 "System.Runtime.CompilerServices.DecimalConstantAttribute",
1760 // compiler generated code
1761 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
1762 // more compiler generated code, e.g. iterator methods
1763 "System.Diagnostics.DebuggerHiddenAttribute",
1764 "System.Runtime.CompilerServices.FixedBufferAttribute",
1765 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
1766 // extension methods
1767 "System.Runtime.CompilerServices.ExtensionAttribute",
1770 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes)
1772 if (!attributes.Any ()) {
1773 ClearElement (root, "Attributes");
1777 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
1781 e = root.OwnerDocument.CreateElement("Attributes");
1783 foreach (string attribute in attributes) {
1784 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
1787 WriteElementText(ae, "AttributeName", attribute);
1790 if (e.ParentNode == null)
1791 root.AppendChild(e);
1793 NormalizeWhitespace(e);
1796 private static string MakeAttributesValueString (object v, TypeReference valueType)
1800 if (valueType.FullName == "System.Type")
1801 return "typeof(" + v.ToString () + ")";
1802 if (valueType.FullName == "System.String")
1803 return "\"" + v.ToString () + "\"";
1805 return (bool)v ? "true" : "false";
1806 TypeDefinition valueDef = valueType.Resolve ();
1807 if (valueDef == null || !valueDef.IsEnum)
1808 return v.ToString ();
1809 string typename = GetDocTypeFullName (valueType);
1810 var values = GetEnumerationValues (valueDef);
1811 long c = ToInt64 (v);
1812 if (values.ContainsKey (c))
1813 return typename + "." + values [c];
1814 if (valueDef.CustomAttributes.Cast<CustomAttribute> ()
1815 .Any (ca => ca.Constructor.DeclaringType.FullName == "System.FlagsAttribute")) {
1816 return string.Join (" | ",
1817 (from i in values.Keys
1819 select typename + "." + values [i])
1822 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
1825 private static Dictionary<long, string> GetEnumerationValues (TypeDefinition type)
1827 var values = new Dictionary<long, string> ();
1829 (from f in type.Fields.Cast<FieldDefinition> ()
1830 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
1832 values [ToInt64 (f.Constant)] = f.Name;
1837 static long ToInt64 (object value)
1840 return (long) (ulong) value;
1841 return Convert.ToInt64 (value);
1844 private void MakeParameters (XmlElement root, ParameterDefinitionCollection parameters)
1846 XmlElement e = WriteElement(root, "Parameters");
1848 foreach (ParameterDefinition p in parameters) {
1849 XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
1851 pe.SetAttribute("Name", p.Name);
1852 pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
1853 if (p.ParameterType is ReferenceType) {
1854 if (p.IsOut) pe.SetAttribute("RefType", "out");
1855 else pe.SetAttribute("RefType", "ref");
1857 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
1861 private void MakeTypeParameters (XmlElement root, GenericParameterCollection typeParams)
1863 if (typeParams == null || typeParams.Count == 0) {
1864 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
1866 root.RemoveChild (f);
1869 XmlElement e = WriteElement(root, "TypeParameters");
1871 foreach (GenericParameter t in typeParams) {
1872 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
1874 pe.SetAttribute("Name", t.Name);
1875 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""));
1876 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
1877 ConstraintCollection constraints = t.Constraints;
1878 GenericParameterAttributes attrs = t.Attributes;
1879 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
1887 ce = root.OwnerDocument.CreateElement ("Constraints");
1889 pe.AppendChild (ce);
1890 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
1891 AppendElementText (ce, "ParameterAttribute", "Contravariant");
1892 if ((attrs & GenericParameterAttributes.Covariant) != 0)
1893 AppendElementText (ce, "ParameterAttribute", "Covariant");
1894 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
1895 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
1896 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
1897 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
1898 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
1899 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
1900 foreach (TypeReference c in constraints) {
1901 TypeDefinition cd = c.Resolve ();
1902 AppendElementText (ce,
1903 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
1904 GetDocTypeFullName (c));
1909 private void MakeParameters (XmlElement root, IMemberReference mi)
1911 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
1912 MakeParameters (root, ((MethodDefinition)mi).Parameters);
1913 else if (mi is MethodDefinition) {
1914 MethodDefinition mb = (MethodDefinition) mi;
1915 ParameterDefinitionCollection parameters = mb.Parameters;
1916 MakeParameters(root, parameters);
1917 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
1918 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
1919 p.SetAttribute ("RefType", "this");
1922 else if (mi is PropertyDefinition) {
1923 ParameterDefinitionCollection parameters = ((PropertyDefinition)mi).Parameters;
1924 if (parameters.Count > 0)
1925 MakeParameters(root, parameters);
1929 else if (mi is FieldDefinition) return;
1930 else if (mi is EventDefinition) return;
1931 else throw new ArgumentException();
1934 internal static string GetDocParameterType (TypeReference type)
1936 return GetDocTypeFullName (type).Replace ("@", "&");
1939 private void MakeReturnValue (XmlElement root, TypeReference type, CustomAttributeCollection attributes)
1941 XmlElement e = WriteElement(root, "ReturnValue");
1943 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
1944 if (attributes != null)
1945 MakeAttributes(e, GetCustomAttributes (attributes, ""));
1948 private void MakeReturnValue (XmlElement root, IMemberReference mi)
1950 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
1952 else if (mi is MethodDefinition)
1953 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType.ReturnType, ((MethodDefinition)mi).ReturnType.CustomAttributes);
1954 else if (mi is PropertyDefinition)
1955 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
1956 else if (mi is FieldDefinition)
1957 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
1958 else if (mi is EventDefinition)
1959 MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
1961 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
1964 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
1966 IMemberReference mi = info.Member;
1967 if (mi is TypeDefinition) return null;
1969 string sigs = MakeMemberSignature(mi);
1970 if (sigs == null) return null; // not publicly visible
1972 // no documentation for property/event accessors. Is there a better way of doing this?
1973 if (mi.Name.StartsWith("get_")) return null;
1974 if (mi.Name.StartsWith("set_")) return null;
1975 if (mi.Name.StartsWith("add_")) return null;
1976 if (mi.Name.StartsWith("remove_")) return null;
1977 if (mi.Name.StartsWith("raise_")) return null;
1979 XmlElement me = doc.CreateElement("Member");
1980 me.SetAttribute("MemberName", GetMemberName (mi));
1984 if (exceptions.HasValue &&
1985 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
1986 UpdateExceptions (info.Node, info.Member);
1988 if (since != null) {
1989 XmlNode docs = me.SelectSingleNode("Docs");
1990 docs.AppendChild (CreateSinceNode (doc));
1996 internal static string GetMemberName (IMemberReference mi)
1998 MethodDefinition mb = mi as MethodDefinition;
2000 PropertyDefinition pi = mi as PropertyDefinition;
2003 return DocUtils.GetPropertyName (pi);
2005 StringBuilder sb = new StringBuilder (mi.Name.Length);
2006 if (!DocUtils.IsExplicitlyImplemented (mb))
2007 sb.Append (mi.Name);
2009 TypeReference iface;
2010 MethodReference ifaceMethod;
2011 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2012 sb.Append (GetDocTypeFullName (iface));
2014 sb.Append (ifaceMethod.Name);
2016 if (mb.IsGenericMethod ()) {
2017 GenericParameterCollection typeParams = mb.GenericParameters;
2018 if (typeParams.Count > 0) {
2020 sb.Append (typeParams [0].Name);
2021 for (int i = 1; i < typeParams.Count; ++i)
2022 sb.Append (",").Append (typeParams [i].Name);
2026 return sb.ToString ();
2029 /// SIGNATURE GENERATION FUNCTIONS
2031 static string MakeTypeSignature (TypeReference type)
2033 return csharpFormatter.GetDeclaration (type);
2036 internal static string MakeMemberSignature (IMemberReference mi)
2038 return csharpFullFormatter.GetDeclaration (mi);
2041 internal static string GetMemberType (IMemberReference mi)
2043 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2044 return "Constructor";
2045 if (mi is MethodDefinition)
2047 if (mi is PropertyDefinition)
2049 if (mi is FieldDefinition)
2051 if (mi is EventDefinition)
2053 throw new ArgumentException();
2056 private static string GetDocTypeName (TypeReference type)
2058 return docTypeFormatter.GetName (type);
2061 internal static string GetDocTypeFullName (TypeReference type)
2063 return DocTypeFullMemberFormatter.Default.GetName (type);
2066 internal static string GetXPathForMember (DocumentationMember member)
2068 StringBuilder xpath = new StringBuilder ();
2069 xpath.Append ("//Members/Member[@MemberName=\"")
2070 .Append (member.MemberName)
2072 if (member.Parameters != null && member.Parameters.Count > 0) {
2073 xpath.Append ("/Parameters[count(Parameter) = ")
2074 .Append (member.Parameters.Count);
2075 for (int i = 0; i < member.Parameters.Count; ++i) {
2076 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2077 xpath.Append (member.Parameters [i]);
2078 xpath.Append ("\"");
2080 xpath.Append ("]/..");
2082 return xpath.ToString ();
2085 public static string GetXPathForMember (XPathNavigator member)
2087 StringBuilder xpath = new StringBuilder ();
2088 xpath.Append ("//Type[@FullName=\"")
2089 .Append (member.SelectSingleNode ("../../@FullName").Value)
2091 xpath.Append ("Members/Member[@MemberName=\"")
2092 .Append (member.SelectSingleNode ("@MemberName").Value)
2094 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2095 if (parameters.Count > 0) {
2096 xpath.Append ("/Parameters[count(Parameter) = ")
2097 .Append (parameters.Count);
2099 while (parameters.MoveNext ()) {
2101 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2102 xpath.Append (parameters.Current.Value);
2103 xpath.Append ("\"");
2105 xpath.Append ("]/..");
2107 return xpath.ToString ();
2110 public static string GetXPathForMember (IMemberReference member)
2112 StringBuilder xpath = new StringBuilder ();
2113 xpath.Append ("//Type[@FullName=\"")
2114 .Append (member.DeclaringType.FullName)
2116 xpath.Append ("Members/Member[@MemberName=\"")
2117 .Append (GetMemberName (member))
2120 ParameterDefinitionCollection parameters = null;
2121 if (member is MethodDefinition)
2122 parameters = ((MethodDefinition) member).Parameters;
2123 else if (member is PropertyDefinition) {
2124 parameters = ((PropertyDefinition) member).Parameters;
2126 if (parameters != null && parameters.Count > 0) {
2127 xpath.Append ("/Parameters[count(Parameter) = ")
2128 .Append (parameters.Count);
2129 for (int i = 0; i < parameters.Count; ++i) {
2130 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2131 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2132 xpath.Append ("\"");
2134 xpath.Append ("]/..");
2136 return xpath.ToString ();
2140 static class CecilExtensions {
2141 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type)
2143 foreach (var c in type.Constructors)
2144 yield return (IMemberReference) c;
2145 foreach (var e in type.Events)
2146 yield return (IMemberReference) e;
2147 foreach (var f in type.Fields)
2148 yield return (IMemberReference) f;
2149 foreach (var m in type.Methods)
2150 yield return (IMemberReference) m;
2151 foreach (var t in type.NestedTypes)
2152 yield return (IMemberReference) t;
2153 foreach (var p in type.Properties)
2154 yield return (IMemberReference) p;
2157 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type, string member)
2159 return GetMembers (type).Where (m => m.Name == member);
2162 public static IMemberReference GetMember (this TypeDefinition type, string member)
2164 return GetMembers (type, member).EnsureZeroOrOne ();
2167 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2169 if (source.Count () > 1)
2170 throw new InvalidOperationException ("too many matches");
2171 return source.FirstOrDefault ();
2174 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2176 return type.Methods.Cast<MethodDefinition> ()
2177 .Where (m => m.Name == method)
2178 .EnsureZeroOrOne ();
2181 public static IEnumerable<IMemberReference> GetDefaultMembers (this TypeReference type)
2183 TypeDefinition def = type as TypeDefinition;
2185 return new IMemberReference [0];
2186 CustomAttribute defMemberAttr = type.CustomAttributes.Cast<CustomAttribute> ()
2187 .Where (c => c.Constructor.DeclaringType.FullName == "System.Reflection.DefaultMemberAttribute")
2189 if (defMemberAttr == null)
2190 return new IMemberReference [0];
2191 string name = (string) defMemberAttr.ConstructorParameters [0];
2192 return def.Properties.Cast<PropertyDefinition> ()
2193 .Where (p => p.Name == name)
2194 .Select (p => (IMemberReference) p);
2197 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2199 return assembly.Modules.Cast<ModuleDefinition> ()
2200 .SelectMany (md => md.Types.Cast<TypeDefinition> ());
2203 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2205 return GetTypes (assembly)
2206 .Where (td => td.FullName == type)
2207 .EnsureZeroOrOne ();
2210 public static bool IsGenericType (this TypeReference type)
2212 return type.GenericParameters.Count > 0;
2215 public static bool IsGenericMethod (this MethodReference method)
2217 return method.GenericParameters.Count > 0;
2220 public static IMemberReference Resolve (this IMemberReference member)
2222 EventReference er = member as EventReference;
2224 return er.Resolve ();
2225 FieldReference fr = member as FieldReference;
2227 return fr.Resolve ();
2228 MethodReference mr = member as MethodReference;
2230 return mr.Resolve ();
2231 PropertyReference pr = member as PropertyReference;
2233 return pr.Resolve ();
2234 TypeReference tr = member as TypeReference;
2236 return tr.Resolve ();
2237 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2241 static class DocUtils {
2242 public static bool IsExplicitlyImplemented (MethodDefinition method)
2244 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2247 public static string GetTypeDotMember (string name)
2249 int startType, startMethod;
2250 startType = startMethod = -1;
2251 for (int i = 0; i < name.Length; ++i) {
2252 if (name [i] == '.') {
2253 startType = startMethod;
2257 return name.Substring (startType+1);
2260 public static string GetMember (string name)
2262 int i = name.LastIndexOf ('.');
2265 return name.Substring (i+1);
2268 public static void GetInfoForExplicitlyImplementedMethod (
2269 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2273 if (method.Overrides.Count != 1)
2274 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2275 iface = method.Overrides [0].DeclaringType;
2276 ifaceMethod = method.Overrides [0];
2279 public static string GetPropertyName (PropertyDefinition pi)
2281 // Issue: (g)mcs-generated assemblies that explicitly implement
2282 // properties don't specify the full namespace, just the
2283 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2284 MethodDefinition method = pi.GetMethod;
2286 method = pi.SetMethod;
2287 if (!IsExplicitlyImplemented (method))
2290 // Need to determine appropriate namespace for this member.
2291 TypeReference iface;
2292 MethodReference ifaceMethod;
2293 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2294 return string.Join (".", new string[]{
2295 DocTypeFullMemberFormatter.Default.GetName (iface),
2296 GetMember (pi.Name)});
2299 public static string GetNamespace (TypeReference type)
2301 if (type.GetOriginalType ().IsNested)
2302 type = type.GetOriginalType ();
2303 while (type != null && type.IsNested)
2304 type = type.DeclaringType;
2306 return string.Empty;
2307 return type.Namespace;
2310 public static string PathCombine (string dir, string path)
2316 return Path.Combine (dir, path);
2319 public static bool IsExtensionMethod (MethodDefinition method)
2322 method.CustomAttributes.Cast<CustomAttribute> ()
2323 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2325 method.DeclaringType.CustomAttributes.Cast<CustomAttribute> ()
2326 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2330 public static bool IsDelegate (TypeDefinition type)
2332 TypeReference baseRef = type.BaseType;
2333 if (baseRef == null)
2335 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2336 baseRef.FullName == "System.MulticastDelegate";
2339 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2341 List<TypeReference> decls = new List<TypeReference> ();
2343 while (type.DeclaringType != null) {
2344 decls.Add (type.DeclaringType);
2345 type = type.DeclaringType;
2351 public static int GetGenericArgumentCount (TypeReference type)
2353 GenericInstanceType inst = type as GenericInstanceType;
2355 ? inst.GenericArguments.Count
2356 : type.GenericParameters.Count;
2359 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
2361 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
2362 List<TypeReference> userInterfaces = new List<TypeReference> ();
2363 foreach (TypeReference iface in type.Interfaces) {
2364 TypeReference lookup = iface.Resolve () ?? iface;
2365 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
2366 userInterfaces.Add (iface);
2368 return userInterfaces;
2371 private static string GetQualifiedTypeName (TypeReference type)
2373 return "[" + type.Scope.Name + "]" + type.FullName;
2376 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
2378 HashSet<string> inheritedInterfaces = new HashSet<string> ();
2379 Action<TypeDefinition> a = null;
2381 if (t == null) return;
2382 foreach (TypeReference r in t.Interfaces) {
2383 inheritedInterfaces.Add (GetQualifiedTypeName (r));
2387 TypeReference baseRef = type.BaseType;
2388 while (baseRef != null) {
2389 TypeDefinition baseDef = baseRef.Resolve ();
2390 if (baseDef != null) {
2392 baseRef = baseDef.BaseType;
2397 foreach (TypeReference r in type.Interfaces)
2399 return inheritedInterfaces;
2403 class DocsNodeInfo {
2404 public DocsNodeInfo (XmlElement node)
2409 public DocsNodeInfo (XmlElement node, TypeDefinition type)
2415 public DocsNodeInfo (XmlElement node, IMemberReference member)
2418 SetMemberInfo (member);
2421 void SetType (TypeDefinition type)
2424 throw new ArgumentNullException ("type");
2426 GenericParameters = new List<GenericParameter> (type.GenericParameters.Cast<GenericParameter> ());
2427 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
2428 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
2429 for (int i = 0; i < declTypes.Count - 1; ++i) {
2430 int remove = System.Math.Min (maxGenArgs,
2431 DocUtils.GetGenericArgumentCount (declTypes [i]));
2432 maxGenArgs -= remove;
2433 while (remove-- > 0)
2434 GenericParameters.RemoveAt (0);
2436 if (DocUtils.IsDelegate (type)) {
2437 Parameters = type.GetMethod("Invoke").Parameters;
2438 ReturnType = type.GetMethod("Invoke").ReturnType.ReturnType;
2442 void SetMemberInfo (IMemberReference member)
2445 throw new ArgumentNullException ("member");
2446 ReturnIsReturn = true;
2450 if (member is MethodReference ) {
2451 MethodReference mr = (MethodReference) member;
2452 Parameters = mr.Parameters;
2453 if (mr.IsGenericMethod ()) {
2454 GenericParameters = new List<GenericParameter> (mr.GenericParameters.Cast<GenericParameter> ());
2457 else if (member is PropertyDefinition) {
2458 Parameters = ((PropertyDefinition) member).Parameters;
2461 if (member is MethodDefinition) {
2462 ReturnType = ((MethodDefinition) member).ReturnType.ReturnType;
2463 } else if (member is PropertyDefinition) {
2464 ReturnType = ((PropertyDefinition) member).PropertyType;
2465 ReturnIsReturn = false;
2468 // no remarks section for enum members
2469 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
2473 public TypeReference ReturnType;
2474 public List<GenericParameter> GenericParameters;
2475 public ParameterDefinitionCollection Parameters;
2476 public bool ReturnIsReturn;
2477 public XmlElement Node;
2478 public bool AddRemarks = true;
2479 public IMemberReference Member;
2480 public TypeDefinition Type;
2481 public string ReturnNodeName;
2484 class DocumentationEnumerator {
2486 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2488 return GetDocumentationTypes (assembly, forTypes, null);
2491 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2493 foreach (TypeDefinition type in assembly.GetTypes()) {
2494 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
2496 if (seen != null && seen.Contains (type.FullName))
2499 foreach (TypeDefinition nested in type.NestedTypes)
2500 yield return nested;
2504 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2506 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
2507 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
2508 oldmember.RemoveAttribute ("__monodocer-seen__");
2511 IMemberReference m = GetMember (type, new DocumentationMember (oldmember));
2513 yield return new DocsNodeInfo (oldmember);
2516 yield return new DocsNodeInfo (oldmember, m);
2521 protected static IMemberReference GetMember (TypeDefinition type, DocumentationMember member)
2523 string membertype = member.MemberType;
2525 string returntype = member.ReturnType;
2527 string docName = member.MemberName;
2528 string[] docTypeParams = GetTypeParameters (docName);
2530 // Loop through all members in this type with the same name
2531 foreach (IMemberReference mi in GetReflectionMembers (type, docName)) {
2532 if (mi is TypeDefinition) continue;
2533 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
2535 string sig = MDocUpdater.MakeMemberSignature(mi);
2536 if (sig == null) continue; // not publicly visible
2538 ParameterDefinitionCollection pis = null;
2539 string[] typeParams = null;
2540 if (mi is MethodDefinition) {
2541 MethodDefinition mb = (MethodDefinition) mi;
2542 pis = mb.Parameters;
2543 if (docTypeParams != null && mb.IsGenericMethod ()) {
2544 GenericParameterCollection args = mb.GenericParameters;
2545 if (args.Count == docTypeParams.Length) {
2546 typeParams = args.Cast<GenericParameter> ().Select (p => p.Name).ToArray ();
2550 else if (mi is PropertyDefinition)
2551 pis = ((PropertyDefinition)mi).Parameters;
2553 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
2554 int pcount = pis == null ? 0 : pis.Count;
2555 if (mcount != pcount)
2558 MethodDefinition mDef = mi as MethodDefinition;
2559 if (mDef != null && !mDef.IsConstructor) {
2560 // Casting operators can overload based on return type.
2561 if (returntype != GetReplacedString (
2562 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType.ReturnType),
2563 typeParams, docTypeParams)) {
2571 for (int i = 0; i < pis.Count; i++) {
2572 string paramType = GetReplacedString (
2573 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
2574 typeParams, docTypeParams);
2575 if (paramType != (string) member.Parameters [i]) {
2580 if (!good) continue;
2588 static string[] GetTypeParameters (string docName)
2590 if (docName [docName.Length-1] != '>')
2592 StringList types = new StringList ();
2593 int endToken = docName.Length-2;
2594 int i = docName.Length-2;
2596 if (docName [i] == ',' || docName [i] == '<') {
2597 types.Add (docName.Substring (i + 1, endToken - i));
2600 if (docName [i] == '<')
2605 return types.ToArray ();
2608 protected static IEnumerable<IMemberReference> GetReflectionMembers (TypeDefinition type, string docName)
2610 // need to worry about 4 forms of //@MemberName values:
2611 // 1. "Normal" (non-generic) member names: GetEnumerator
2613 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
2614 // - try as-is, and try type.member (due to "kludge" for property
2616 // 3. "Normal" Generic member names: Sort<T> (CSC)
2617 // - need to remove generic parameters --> "Sort"
2618 // 4. Explicitly-implemented interface members for generic interfaces:
2619 // -- System.Collections.Generic.IEnumerable<T>.Current
2620 // - Try as-is, and try type.member, *keeping* the generic parameters.
2621 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
2622 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
2623 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
2624 // this as (1) or (2).
2625 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
2627 foreach (IMemberReference mi in type.GetMembers (docName))
2629 if (CountChars (docName, '.') > 0)
2630 // might be a property; try only type.member instead of
2631 // namespace.type.member.
2632 foreach (IMemberReference mi in
2633 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
2640 int startLt, startType, startMethod;
2641 startLt = startType = startMethod = -1;
2642 for (int i = 0; i < docName.Length; ++i) {
2643 switch (docName [i]) {
2652 if (numLt == 0 && (i + 1) < docName.Length)
2653 // there's another character in docName, so this <...> sequence is
2654 // probably part of a generic type -- case 4.
2658 startType = startMethod;
2664 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
2666 foreach (IMemberReference mi in type.GetMembers (refName))
2670 foreach (IMemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
2673 // If we _still_ haven't found it, we've hit another generic naming issue:
2674 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
2675 // explicitly-implemented METHOD names (not properties), e.g.
2676 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
2677 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
2678 // which the XML docs will contain.
2680 // Alas, we can't derive the Mono name from docName, so we need to iterate
2681 // over all member names, convert them into CSC format, and compare... :-(
2684 foreach (IMemberReference mi in type.GetMembers ()) {
2685 if (MDocUpdater.GetMemberName (mi) == docName)
2690 static string GetReplacedString (string typeName, string[] from, string[] to)
2694 for (int i = 0; i < from.Length; ++i)
2695 typeName = typeName.Replace (from [i], to [i]);
2699 private static int CountChars (string s, char c)
2702 for (int i = 0; i < s.Length; ++i) {
2710 class EcmaDocumentationEnumerator : DocumentationEnumerator {
2715 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
2718 this.ecmadocs = ecmaDocs;
2721 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2723 HashSet<string> seen = new HashSet<string> ();
2724 return GetDocumentationTypes (assembly, forTypes, seen)
2725 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
2728 IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2731 while (ecmadocs.Read ()) {
2732 switch (ecmadocs.Name) {
2734 if (typeDepth == -1)
2735 typeDepth = ecmadocs.Depth;
2736 if (ecmadocs.NodeType != XmlNodeType.Element)
2738 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
2740 string typename = ecmadocs.GetAttribute ("FullName");
2741 string typename2 = MDocUpdater.GetTypeFileName (typename);
2742 if (forTypes != null &&
2743 forTypes.BinarySearch (typename) < 0 &&
2744 typename != typename2 &&
2745 forTypes.BinarySearch (typename2) < 0)
2748 if ((t = assembly.GetType (typename)) == null &&
2749 (t = assembly.GetType (typename2)) == null)
2751 seen.Add (typename);
2752 if (typename != typename2)
2753 seen.Add (typename2);
2754 Console.WriteLine (" Import: {0}", t.FullName);
2755 if (ecmadocs.Name != "Docs") {
2756 int depth = ecmadocs.Depth;
2757 while (ecmadocs.Read ()) {
2758 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
2762 if (!ecmadocs.IsStartElement ("Docs"))
2763 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
2773 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2775 return GetMembers (basefile, type)
2776 .Concat (base.GetDocumentationMembers (basefile, type));
2779 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
2781 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
2784 if (ecmadocs.IsEmptyElement)
2787 int membersDepth = ecmadocs.Depth;
2789 while (go && ecmadocs.Read ()) {
2790 switch (ecmadocs.Name) {
2792 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
2794 DocumentationMember dm = new DocumentationMember (ecmadocs);
2795 string xp = MDocUpdater.GetXPathForMember (dm);
2796 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
2798 if (oldmember == null) {
2799 m = GetMember (type, dm);
2801 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2802 type.FullName, dm.MemberSignatures ["C#"]);
2803 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
2806 // oldmember lookup may have failed due to type parameter renames.
2808 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
2809 if (oldmember == null) {
2810 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
2811 oldmember = basefile.CreateElement ("Member");
2812 oldmember.SetAttribute ("MemberName", dm.MemberName);
2813 members.AppendChild (oldmember);
2814 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
2815 XmlElement ms = basefile.CreateElement ("MemberSignature");
2816 ms.SetAttribute ("Language", key);
2817 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
2818 oldmember.AppendChild (ms);
2820 oldmember.SetAttribute ("__monodocer-seen__", "true");
2821 Console.WriteLine ("Member Added: {0}", MDocUpdater.MakeMemberSignature (m));
2826 m = GetMember (type, new DocumentationMember (oldmember));
2828 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2829 type.FullName, dm.MemberSignatures ["C#"]);
2832 oldmember.SetAttribute ("__monodocer-seen__", "true");
2834 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
2835 if (ecmadocs.Name != "Docs")
2836 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
2841 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
2850 abstract class DocumentationImporter {
2852 public abstract void ImportDocumentation (DocsNodeInfo info);
2855 class MsxdocDocumentationImporter : DocumentationImporter {
2857 XmlDocument slashdocs;
2859 public MsxdocDocumentationImporter (string file)
2861 var xml = File.ReadAllText (file);
2863 // Ensure Unix line endings
2864 xml = xml.Replace ("\r", "");
2866 slashdocs = new XmlDocument();
2867 slashdocs.LoadXml (xml);
2870 public override void ImportDocumentation (DocsNodeInfo info)
2872 XmlNode elem = GetDocs (info.Member ?? info.Type);
2877 XmlElement e = info.Node;
2879 if (elem.SelectSingleNode("summary") != null)
2880 MDocUpdater.ClearElement(e, "summary");
2881 if (elem.SelectSingleNode("remarks") != null)
2882 MDocUpdater.ClearElement(e, "remarks");
2883 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
2884 MDocUpdater.ClearElement(e, "value");
2885 MDocUpdater.ClearElement(e, "returns");
2888 foreach (XmlNode child in elem.ChildNodes) {
2889 switch (child.Name) {
2892 XmlAttribute name = child.Attributes ["name"];
2895 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
2897 p2.InnerXml = child.InnerXml;
2900 // Occasionally XML documentation will use <returns/> on
2901 // properties, so let's try to normalize things.
2904 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnNodeName ?? child.Name);
2905 v.InnerXml = child.InnerXml;
2911 case "permission": {
2912 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
2915 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
2917 a = e.OwnerDocument.CreateElement (child.Name);
2918 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
2921 a.InnerXml = child.InnerXml;
2925 XmlAttribute cref = child.Attributes ["cref"];
2928 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
2930 a = e.OwnerDocument.CreateElement ("altmember");
2931 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
2938 if (child.NodeType == XmlNodeType.Element &&
2939 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
2942 MDocUpdater.CopyNode (child, e);
2949 private XmlNode GetDocs (IMemberReference member)
2951 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
2952 if (slashdocsig != null)
2953 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
2958 class EcmaDocumentationImporter : DocumentationImporter {
2962 public EcmaDocumentationImporter (XmlReader ecmaDocs)
2964 this.ecmadocs = ecmaDocs;
2967 public override void ImportDocumentation (DocsNodeInfo info)
2969 if (!ecmadocs.IsStartElement ("Docs")) {
2973 XmlElement e = info.Node;
2975 int depth = ecmadocs.Depth;
2976 ecmadocs.ReadStartElement ("Docs");
2977 while (ecmadocs.Read ()) {
2978 if (ecmadocs.Name == "Docs") {
2979 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
2982 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
2984 if (!ecmadocs.IsStartElement ())
2986 switch (ecmadocs.Name) {
2989 string name = ecmadocs.GetAttribute ("name");
2992 XmlNode doc = e.SelectSingleNode (
2993 ecmadocs.Name + "[@name='" + name + "']");
2994 string value = ecmadocs.ReadInnerXml ();
2996 doc.InnerXml = value.Replace ("\r", "");
3003 string name = ecmadocs.Name;
3004 string cref = ecmadocs.GetAttribute ("cref");
3007 XmlNode doc = e.SelectSingleNode (
3008 ecmadocs.Name + "[@cref='" + cref + "']");
3009 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3011 doc.InnerXml = value;
3013 XmlElement n = e.OwnerDocument.CreateElement (name);
3014 n.SetAttribute ("cref", cref);
3021 string name = ecmadocs.Name;
3022 string xpath = ecmadocs.Name;
3023 StringList attributes = new StringList (ecmadocs.AttributeCount);
3024 if (ecmadocs.MoveToFirstAttribute ()) {
3026 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3027 } while (ecmadocs.MoveToNextAttribute ());
3028 ecmadocs.MoveToContent ();
3030 if (attributes.Count > 0) {
3031 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3033 XmlNode doc = e.SelectSingleNode (xpath);
3034 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3036 doc.InnerXml = value;
3039 XmlElement n = e.OwnerDocument.CreateElement (name);
3041 foreach (string a in attributes) {
3042 int eq = a.IndexOf ('=');
3043 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3054 class DocumentationMember {
3055 public StringToStringMap MemberSignatures = new StringToStringMap ();
3056 public string ReturnType;
3057 public StringList Parameters;
3058 public string MemberName;
3059 public string MemberType;
3061 public DocumentationMember (XmlReader reader)
3063 MemberName = reader.GetAttribute ("MemberName");
3064 int depth = reader.Depth;
3066 StringList p = new StringList ();
3068 if (reader.NodeType != XmlNodeType.Element)
3070 switch (reader.Name) {
3071 case "MemberSignature":
3072 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3075 MemberType = reader.ReadElementString ();
3078 if (reader.Depth == depth + 2)
3079 ReturnType = reader.ReadElementString ();
3082 if (reader.Depth == depth + 2)
3083 p.Add (reader.GetAttribute ("Type"));
3086 if (reader.Depth == depth + 1)
3090 } while (go && reader.Read () && reader.Depth >= depth);
3096 public DocumentationMember (XmlNode node)
3098 MemberName = node.Attributes ["MemberName"].Value;
3099 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3100 XmlAttribute l = n.Attributes ["Language"];
3101 XmlAttribute v = n.Attributes ["Value"];
3102 if (l != null && v != null)
3103 MemberSignatures [l.Value] = v.Value;
3105 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3106 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
3108 ReturnType = rt.InnerText;
3109 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
3111 Parameters = new StringList (p.Count);
3112 for (int i = 0; i < p.Count; ++i)
3113 Parameters.Add (p [i].Attributes ["Type"].Value);
3118 public enum MemberFormatterState {
3121 WithinGenericTypeContainer,
3124 public abstract class MemberFormatter {
3125 public virtual string GetName (IMemberReference member)
3127 TypeReference type = member as TypeReference;
3129 return GetTypeName (type);
3130 MethodReference method = member as MethodReference;
3131 if (method != null && method.Name == ".ctor") // method.IsConstructor
3132 return GetConstructorName (method);
3134 return GetMethodName (method);
3135 PropertyReference prop = member as PropertyReference;
3137 return GetPropertyName (prop);
3138 FieldReference field = member as FieldReference;
3140 return GetFieldName (field);
3141 EventReference e = member as EventReference;
3143 return GetEventName (e);
3144 throw new NotSupportedException ("Can't handle: " +
3145 (member == null ? "null" : member.GetType().ToString()));
3148 protected virtual string GetTypeName (TypeReference type)
3151 throw new ArgumentNullException ("type");
3152 return _AppendTypeName (new StringBuilder (type.Name.Length), type).ToString ();
3155 protected virtual char[] ArrayDelimeters {
3156 get {return new char[]{'[', ']'};}
3159 protected virtual MemberFormatterState MemberFormatterState { get; set; }
3161 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type)
3163 if (type is ArrayType) {
3164 TypeSpecification spec = type as TypeSpecification;
3165 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3166 .Append (ArrayDelimeters [0]);
3167 var origState = MemberFormatterState;
3168 MemberFormatterState = MemberFormatterState.WithinArray;
3169 ArrayType array = (ArrayType) type;
3170 int rank = array.Rank;
3172 buf.Append (new string (',', rank-1));
3173 MemberFormatterState = origState;
3174 return buf.Append (ArrayDelimeters [1]);
3176 if (type is ReferenceType) {
3177 return AppendRefTypeName (buf, type);
3179 if (type is PointerType) {
3180 return AppendPointerTypeName (buf, type);
3182 AppendNamespace (buf, type);
3183 if (type is GenericParameter) {
3184 return AppendTypeName (buf, type);
3186 GenericInstanceType genInst = type as GenericInstanceType;
3187 if (type.GenericParameters.Count == 0 &&
3188 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3189 return AppendFullTypeName (buf, type);
3191 return AppendGenericType (buf, type);
3194 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3196 string ns = DocUtils.GetNamespace (type);
3197 if (ns != null && ns.Length > 0)
3198 buf.Append (ns).Append ('.');
3202 private StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type)
3204 if (type.DeclaringType != null)
3205 AppendFullTypeName (buf, type.DeclaringType).Append (NestedTypeSeparator);
3206 return AppendTypeName (buf, type);
3209 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3211 return AppendTypeName (buf, type.Name);
3214 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3216 int n = typename.IndexOf ("`");
3218 return buf.Append (typename.Substring (0, n));
3219 return buf.Append (typename);
3222 protected virtual string RefTypeModifier {
3226 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type)
3228 TypeSpecification spec = type as TypeSpecification;
3229 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3230 .Append (RefTypeModifier);
3233 protected virtual string PointerModifier {
3237 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type)
3239 TypeSpecification spec = type as TypeSpecification;
3240 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3241 .Append (PointerModifier);
3244 protected virtual char[] GenericTypeContainer {
3245 get {return new char[]{'<', '>'};}
3248 protected virtual char NestedTypeSeparator {
3252 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3254 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3255 type is GenericInstanceType ? type.GetOriginalType () : type);
3256 List<TypeReference> genArgs = GetGenericArguments (type);
3259 bool insertNested = false;
3260 foreach (var decl in decls) {
3261 TypeReference declDef = decl.Resolve () ?? decl;
3263 buf.Append (NestedTypeSeparator);
3265 insertNested = true;
3266 AppendTypeName (buf, declDef);
3267 int ac = DocUtils.GetGenericArgumentCount (declDef);
3271 buf.Append (GenericTypeContainer [0]);
3272 var origState = MemberFormatterState;
3273 MemberFormatterState = MemberFormatterState.WithinGenericTypeContainer;
3274 _AppendTypeName (buf, genArgs [argIdx++]);
3275 for (int i = 1; i < c; ++i)
3276 _AppendTypeName (buf.Append (","), genArgs [argIdx++]);
3277 MemberFormatterState = origState;
3278 buf.Append (GenericTypeContainer [1]);
3284 private List<TypeReference> GetGenericArguments (TypeReference type)
3286 var args = new List<TypeReference> ();
3287 GenericInstanceType inst = type as GenericInstanceType;
3289 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
3291 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
3295 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3300 protected virtual string GetConstructorName (MethodReference constructor)
3302 return constructor.Name;
3305 protected virtual string GetMethodName (MethodReference method)
3310 protected virtual string GetPropertyName (PropertyReference property)
3312 return property.Name;
3315 protected virtual string GetFieldName (FieldReference field)
3320 protected virtual string GetEventName (EventReference e)
3325 public virtual string GetDeclaration (IMemberReference member)
3328 throw new ArgumentNullException ("member");
3329 TypeDefinition type = member as TypeDefinition;
3331 return GetTypeDeclaration (type);
3332 MethodDefinition method = member as MethodDefinition;
3333 if (method != null && method.IsConstructor)
3334 return GetConstructorDeclaration (method);
3336 return GetMethodDeclaration (method);
3337 PropertyDefinition prop = member as PropertyDefinition;
3339 return GetPropertyDeclaration (prop);
3340 FieldDefinition field = member as FieldDefinition;
3342 return GetFieldDeclaration (field);
3343 EventDefinition e = member as EventDefinition;
3345 return GetEventDeclaration (e);
3346 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3349 protected virtual string GetTypeDeclaration (TypeDefinition type)
3352 throw new ArgumentNullException ("type");
3353 StringBuilder buf = new StringBuilder (type.Name.Length);
3354 _AppendTypeName (buf, type);
3355 AppendGenericTypeConstraints (buf, type);
3356 return buf.ToString ();
3359 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
3361 return GetConstructorName (constructor);
3364 protected virtual string GetMethodDeclaration (MethodDefinition method)
3366 // Special signature for destructors.
3367 if (method.Name == "Finalize" && method.Parameters.Count == 0)
3368 return GetFinalizerName (method);
3370 StringBuilder buf = new StringBuilder ();
3372 AppendVisibility (buf, method);
3373 if (buf.Length == 0 &&
3374 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3377 AppendModifiers (buf, method);
3379 if (buf.Length != 0)
3381 buf.Append (GetName (method.ReturnType.ReturnType)).Append (" ");
3383 AppendMethodName (buf, method);
3384 AppendGenericMethod (buf, method).Append (" ");
3385 AppendParameters (buf, method, method.Parameters);
3386 AppendGenericMethodConstraints (buf, method);
3387 return buf.ToString ();
3390 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3392 return buf.Append (method.Name);
3395 protected virtual string GetFinalizerName (MethodDefinition method)
3400 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3405 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3410 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3415 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3420 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3425 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
3427 return GetPropertyName (property);
3430 protected virtual string GetFieldDeclaration (FieldDefinition field)
3432 return GetFieldName (field);
3435 protected virtual string GetEventDeclaration (EventDefinition e)
3437 return GetEventName (e);
3441 class CSharpFullMemberFormatter : MemberFormatter {
3443 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3445 string ns = DocUtils.GetNamespace (type);
3446 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
3447 buf.Append (ns).Append ('.');
3451 private string GetCSharpType (string t)
3454 case "System.Byte": return "byte";
3455 case "System.SByte": return "sbyte";
3456 case "System.Int16": return "short";
3457 case "System.Int32": return "int";
3458 case "System.Int64": return "long";
3460 case "System.UInt16": return "ushort";
3461 case "System.UInt32": return "uint";
3462 case "System.UInt64": return "ulong";
3464 case "System.Single": return "float";
3465 case "System.Double": return "double";
3466 case "System.Decimal": return "decimal";
3467 case "System.Boolean": return "bool";
3468 case "System.Char": return "char";
3469 case "System.Void": return "void";
3470 case "System.String": return "string";
3471 case "System.Object": return "object";
3476 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3478 if (type is GenericParameter)
3479 return AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
3480 string t = type.FullName;
3481 if (!t.StartsWith ("System.")) {
3482 return base.AppendTypeName (buf, type);
3485 string s = GetCSharpType (t);
3487 return buf.Append (s);
3489 return base.AppendTypeName (buf, type);
3492 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
3494 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeContainer)
3496 GenericParameterAttributes attrs = type.Attributes;
3497 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
3498 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
3502 buf.Append ("out ");
3506 protected override string GetTypeDeclaration (TypeDefinition type)
3508 string visibility = GetTypeVisibility (type.Attributes);
3509 if (visibility == null)
3512 StringBuilder buf = new StringBuilder ();
3514 buf.Append (visibility);
3517 MemberFormatter full = new CSharpFullMemberFormatter ();
3519 if (DocUtils.IsDelegate (type)) {
3520 buf.Append("delegate ");
3521 MethodDefinition invoke = type.GetMethod ("Invoke");
3522 buf.Append (full.GetName (invoke.ReturnType.ReturnType)).Append (" ");
3523 buf.Append (GetName (type));
3524 AppendParameters (buf, invoke, invoke.Parameters);
3525 AppendGenericTypeConstraints (buf, type);
3528 return buf.ToString();
3531 if (type.IsAbstract && !type.IsInterface)
3532 buf.Append("abstract ");
3533 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
3534 buf.Append("sealed ");
3535 buf.Replace ("abstract sealed", "static");
3537 buf.Append (GetTypeKind (type));
3539 buf.Append (GetCSharpType (type.FullName) == null
3544 TypeReference basetype = type.BaseType;
3545 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
3548 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
3549 .Select (iface => full.GetName (iface))
3553 if (basetype != null || interface_names.Count > 0)
3556 if (basetype != null) {
3557 buf.Append (full.GetName (basetype));
3558 if (interface_names.Count > 0)
3562 for (int i = 0; i < interface_names.Count; i++){
3565 buf.Append (interface_names [i]);
3567 AppendGenericTypeConstraints (buf, type);
3570 return buf.ToString ();
3573 static string GetTypeKind (TypeDefinition t)
3579 if (t.IsClass || t.FullName == "System.Enum")
3583 throw new ArgumentException(t.FullName);
3586 static string GetTypeVisibility (TypeAttributes ta)
3588 switch (ta & TypeAttributes.VisibilityMask) {
3589 case TypeAttributes.Public:
3590 case TypeAttributes.NestedPublic:
3593 case TypeAttributes.NestedFamily:
3594 case TypeAttributes.NestedFamORAssem:
3602 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3604 if (type.GenericParameters.Count == 0)
3606 return AppendConstraints (buf, type.GenericParameters);
3609 private StringBuilder AppendConstraints (StringBuilder buf, GenericParameterCollection genArgs)
3611 foreach (GenericParameter genArg in genArgs) {
3612 GenericParameterAttributes attrs = genArg.Attributes;
3613 ConstraintCollection constraints = genArg.Constraints;
3614 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
3617 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
3618 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
3619 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
3622 if (!isref && !isvt && !isnew && constraints.Count == 0)
3624 buf.Append (" where ").Append (genArg.Name).Append (" : ");
3626 buf.Append ("class");
3630 buf.Append ("struct");
3633 if (constraints.Count > 0 && !isvt) {
3636 buf.Append (GetTypeName (constraints [0]));
3637 for (int i = 1; i < constraints.Count; ++i)
3638 buf.Append (", ").Append (GetTypeName (constraints [i]));
3640 if (isnew && !isvt) {
3643 buf.Append ("new()");
3649 protected override string GetConstructorDeclaration (MethodDefinition constructor)
3651 StringBuilder buf = new StringBuilder ();
3652 AppendVisibility (buf, constructor);
3653 if (buf.Length == 0)
3657 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
3658 AppendParameters (buf, constructor, constructor.Parameters);
3661 return buf.ToString ();
3664 protected override string GetMethodDeclaration (MethodDefinition method)
3666 string decl = base.GetMethodDeclaration (method);
3672 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3674 if (DocUtils.IsExplicitlyImplemented (method)) {
3675 TypeReference iface;
3676 MethodReference ifaceMethod;
3677 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3678 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3680 .Append (ifaceMethod.Name);
3682 return base.AppendMethodName (buf, method);
3685 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3687 if (method.GenericParameters.Count == 0)
3689 return AppendConstraints (buf, method.GenericParameters);
3692 protected override string RefTypeModifier {
3696 protected override string GetFinalizerName (MethodDefinition method)
3698 return "~" + method.DeclaringType.Name + " ()";
3701 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3705 if (method.IsPublic)
3706 return buf.Append ("public");
3707 if (method.IsFamily || method.IsFamilyOrAssembly)
3708 return buf.Append ("protected");
3712 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3714 string modifiers = String.Empty;
3715 if (method.IsStatic) modifiers += " static";
3716 if (method.IsVirtual && !method.IsAbstract) {
3717 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3718 else modifiers += " override";
3720 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
3721 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
3722 if (method.IsFinal) modifiers += " sealed";
3723 if (modifiers == " virtual sealed") modifiers = "";
3725 return buf.Append (modifiers);
3728 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3730 if (method.IsGenericMethod ()) {
3731 GenericParameterCollection args = method.GenericParameters;
3732 if (args.Count > 0) {
3734 buf.Append (args [0].Name);
3735 for (int i = 1; i < args.Count; ++i)
3736 buf.Append (",").Append (args [i].Name);
3743 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3745 return AppendParameters (buf, method, parameters, '(', ')');
3748 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters, char begin, char end)
3752 if (parameters.Count > 0) {
3753 if (DocUtils.IsExtensionMethod (method))
3754 buf.Append ("this ");
3755 AppendParameter (buf, parameters [0]);
3756 for (int i = 1; i < parameters.Count; ++i) {
3758 AppendParameter (buf, parameters [i]);
3762 return buf.Append (end);
3765 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
3767 if (parameter.ParameterType is ReferenceType) {
3768 if (parameter.IsOut)
3769 buf.Append ("out ");
3771 buf.Append ("ref ");
3773 buf.Append (GetName (parameter.ParameterType)).Append (" ");
3774 return buf.Append (parameter.Name);
3777 protected override string GetPropertyDeclaration (PropertyDefinition property)
3779 MethodDefinition method;
3781 string get_visible = null;
3782 if ((method = property.GetMethod) != null &&
3783 (DocUtils.IsExplicitlyImplemented (method) ||
3784 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3785 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3786 string set_visible = null;
3787 if ((method = property.SetMethod) != null &&
3788 (DocUtils.IsExplicitlyImplemented (method) ||
3789 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3790 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3792 if ((set_visible == null) && (get_visible == null))
3796 StringBuilder buf = new StringBuilder ();
3797 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
3798 buf.Append (visibility = get_visible);
3799 else if (set_visible != null && get_visible == null)
3800 buf.Append (visibility = set_visible);
3802 buf.Append (visibility = "public");
3804 // Pick an accessor to use for static/virtual/override/etc. checks.
3805 method = property.SetMethod;
3807 method = property.GetMethod;
3809 string modifiers = String.Empty;
3810 if (method.IsStatic) modifiers += " static";
3811 if (method.IsVirtual && !method.IsAbstract) {
3812 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
3813 modifiers += " virtual";
3815 modifiers += " override";
3817 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
3818 if (method.IsAbstract && !declDef.IsInterface)
3819 modifiers += " abstract";
3821 modifiers += " sealed";
3822 if (modifiers == " virtual sealed")
3824 buf.Append (modifiers).Append (' ');
3826 buf.Append (GetName (property.PropertyType)).Append (' ');
3828 IEnumerable<IMemberReference> defs = property.DeclaringType.GetDefaultMembers ();
3829 string name = property.Name;
3830 foreach (IMemberReference mi in defs) {
3831 if (mi == property) {
3836 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
3838 if (property.Parameters.Count != 0) {
3839 AppendParameters (buf, method, property.Parameters, '[', ']');
3843 if (set_visible != null) {
3844 if (set_visible != visibility)
3845 buf.Append (' ').Append (set_visible);
3846 buf.Append (" set;");
3848 if (get_visible != null) {
3849 if (get_visible != visibility)
3850 buf.Append (' ').Append (get_visible);
3851 buf.Append (" get;");
3855 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
3858 protected override string GetFieldDeclaration (FieldDefinition field)
3860 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
3861 if (declType.IsEnum && field.Name == "value__")
3862 return null; // This member of enums aren't documented.
3864 StringBuilder buf = new StringBuilder ();
3865 AppendFieldVisibility (buf, field);
3866 if (buf.Length == 0)
3869 if (declType.IsEnum)
3872 if (field.IsStatic && !field.IsLiteral)
3873 buf.Append (" static");
3874 if (field.IsInitOnly)
3875 buf.Append (" readonly");
3876 if (field.IsLiteral)
3877 buf.Append (" const");
3879 buf.Append (' ').Append (GetName (field.FieldType)).Append (' ');
3880 buf.Append (field.Name);
3881 AppendFieldValue (buf, field);
3884 return buf.ToString ();
3887 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
3890 return buf.Append ("public");
3891 if (field.IsFamily || field.IsFamilyOrAssembly)
3892 return buf.Append ("protected");
3896 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
3898 // enums have a value__ field, which we ignore
3899 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
3900 field.DeclaringType.IsGenericType ())
3902 if (field.HasConstant && field.IsLiteral) {
3905 val = field.Constant;
3910 buf.Append (" = ").Append ("null");
3911 else if (val is Enum)
3912 buf.Append (" = ").Append (val.ToString ());
3913 else if (val is IFormattable) {
3914 string value = ((IFormattable)val).ToString();
3916 value = "\"" + value + "\"";
3917 buf.Append (" = ").Append (value);
3923 protected override string GetEventDeclaration (EventDefinition e)
3925 StringBuilder buf = new StringBuilder ();
3926 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
3930 AppendModifiers (buf, e.AddMethod);
3932 buf.Append (" event ");
3933 buf.Append (GetName (e.EventType)).Append (' ');
3934 buf.Append (e.Name).Append (';');
3936 return buf.ToString ();
3940 class CSharpMemberFormatter : CSharpFullMemberFormatter {
3941 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3947 class DocTypeFullMemberFormatter : MemberFormatter {
3948 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
3950 protected override char NestedTypeSeparator {
3955 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
3956 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3962 class SlashDocMemberFormatter : MemberFormatter {
3964 protected override char[] GenericTypeContainer {
3965 get {return new char[]{'{', '}'};}
3968 private bool AddTypeCount = true;
3970 private TypeReference genDeclType;
3971 private MethodReference genDeclMethod;
3973 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3975 if (type is GenericParameter) {
3977 if (genDeclType != null) {
3978 GenericParameterCollection genArgs = genDeclType.GenericParameters;
3979 for (int i = 0; i < genArgs.Count; ++i) {
3980 if (genArgs [i].Name == type.Name) {
3981 buf.Append ('`').Append (i);
3986 if (genDeclMethod != null) {
3987 GenericParameterCollection genArgs = null;
3988 if (genDeclMethod.IsGenericMethod ()) {
3989 genArgs = genDeclMethod.GenericParameters;
3990 for (int i = 0; i < genArgs.Count; ++i) {
3991 if (genArgs [i].Name == type.Name) {
3992 buf.Append ("``").Append (i);
3998 if (genDeclType == null && genDeclMethod == null) {
3999 // Probably from within an explicitly implemented interface member,
4000 // where CSC uses parameter names instead of indices (why?), e.g.
4001 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
4002 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
4003 buf.Append (type.Name);
4005 if (buf.Length == l) {
4006 throw new Exception (string.Format (
4007 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
4008 type.Name, genDeclType, genDeclMethod));
4012 base.AppendTypeName (buf, type);
4014 int numArgs = type.GenericParameters.Count;
4015 if (type.DeclaringType != null)
4016 numArgs -= type.GenericParameters.Count;
4018 buf.Append ('`').Append (numArgs);
4025 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
4028 base.AppendGenericType (buf, type);
4030 AppendType (buf, type);
4034 private StringBuilder AppendType (StringBuilder buf, TypeReference type)
4036 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
4037 bool insertNested = false;
4038 int prevParamCount = 0;
4039 foreach (var decl in decls) {
4041 buf.Append (NestedTypeSeparator);
4042 insertNested = true;
4043 base.AppendTypeName (buf, decl);
4044 int argCount = DocUtils.GetGenericArgumentCount (decl);
4045 int numArgs = argCount - prevParamCount;
4046 prevParamCount = argCount;
4048 buf.Append ('`').Append (numArgs);
4053 public override string GetDeclaration (IMemberReference member)
4055 TypeReference r = member as TypeReference;
4057 return "T:" + GetTypeName (r);
4059 return base.GetDeclaration (member);
4062 protected override string GetConstructorName (MethodReference constructor)
4064 return GetMethodDefinitionName (constructor, "#ctor");
4067 protected override string GetMethodName (MethodReference method)
4070 MethodDefinition methodDef = method as MethodDefinition;
4071 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
4074 TypeReference iface;
4075 MethodReference ifaceMethod;
4076 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
4077 AddTypeCount = false;
4078 name = GetTypeName (iface) + "." + ifaceMethod.Name;
4079 AddTypeCount = true;
4081 return GetMethodDefinitionName (method, name);
4084 private string GetMethodDefinitionName (MethodReference method, string name)
4086 StringBuilder buf = new StringBuilder ();
4087 buf.Append (GetTypeName (method.DeclaringType));
4089 buf.Append (name.Replace (".", "#"));
4090 if (method.IsGenericMethod ()) {
4091 GenericParameterCollection genArgs = method.GenericParameters;
4092 if (genArgs.Count > 0)
4093 buf.Append ("``").Append (genArgs.Count);
4095 ParameterDefinitionCollection parameters = method.Parameters;
4097 genDeclType = method.DeclaringType;
4098 genDeclMethod = method;
4099 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
4103 genDeclMethod = null;
4105 return buf.ToString ();
4108 private StringBuilder AppendParameters (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinitionCollection parameters)
4110 if (parameters.Count == 0)
4115 AppendParameter (buf, genArgs, parameters [0]);
4116 for (int i = 1; i < parameters.Count; ++i) {
4118 AppendParameter (buf, genArgs, parameters [i]);
4121 return buf.Append (')');
4124 private StringBuilder AppendParameter (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinition parameter)
4126 AddTypeCount = false;
4127 buf.Append (GetTypeName (parameter.ParameterType));
4128 AddTypeCount = true;
4132 protected override string GetPropertyName (PropertyReference property)
4136 PropertyDefinition propertyDef = property as PropertyDefinition;
4137 MethodDefinition method = null;
4138 if (propertyDef != null)
4139 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
4140 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
4141 name = property.Name;
4143 TypeReference iface;
4144 MethodReference ifaceMethod;
4145 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4146 AddTypeCount = false;
4147 name = string.Join ("#", new string[]{
4148 GetTypeName (iface).Replace (".", "#"),
4149 DocUtils.GetMember (property.Name)
4151 AddTypeCount = true;
4154 StringBuilder buf = new StringBuilder ();
4155 buf.Append (GetName (property.DeclaringType));
4158 ParameterDefinitionCollection parameters = property.Parameters;
4159 if (parameters.Count > 0) {
4160 genDeclType = property.DeclaringType;
4162 GenericParameterCollection genArgs = property.DeclaringType.GenericParameters;
4163 AppendParameter (buf, genArgs, parameters [0]);
4164 for (int i = 1; i < parameters.Count; ++i) {
4166 AppendParameter (buf, genArgs, parameters [i]);
4171 return buf.ToString ();
4174 protected override string GetFieldName (FieldReference field)
4176 return string.Format ("{0}.{1}",
4177 GetName (field.DeclaringType), field.Name);
4180 protected override string GetEventName (EventReference e)
4182 return string.Format ("{0}.{1}",
4183 GetName (e.DeclaringType), e.Name);
4186 protected override string GetTypeDeclaration (TypeDefinition type)
4188 string name = GetName (type);
4194 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4196 string name = GetName (constructor);
4202 protected override string GetMethodDeclaration (MethodDefinition method)
4204 string name = GetName (method);
4207 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4208 genDeclType = method.DeclaringType;
4209 genDeclMethod = method;
4210 name += "~" + GetName (method.ReturnType.ReturnType);
4212 genDeclMethod = null;
4217 protected override string GetPropertyDeclaration (PropertyDefinition property)
4219 string name = GetName (property);
4225 protected override string GetFieldDeclaration (FieldDefinition field)
4227 string name = GetName (field);
4233 protected override string GetEventDeclaration (EventDefinition e)
4235 string name = GetName (e);
4242 class FileNameMemberFormatter : SlashDocMemberFormatter {
4243 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4248 protected override char NestedTypeSeparator {