1 // Updater program for syncing Mono's ECMA-style documentation files
3 // By Joshua Tauberer <tauberer@for.net>
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Diagnostics;
9 using System.Globalization;
14 using System.Xml.XPath;
19 using MyXmlNodeList = System.Collections.Generic.List<System.Xml.XmlNode>;
20 using StringList = System.Collections.Generic.List<string>;
21 using StringToStringMap = System.Collections.Generic.Dictionary<string, string>;
22 using StringToXmlNodeMap = System.Collections.Generic.Dictionary<string, System.Xml.XmlNode>;
24 namespace Mono.Documentation {
26 class MDocUpdater : MDocCommand
29 List<AssemblyDefinition> assemblies;
30 readonly DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver();
34 bool no_assembly_versions;
35 ExceptionLocations? exceptions;
37 internal int additions = 0, deletions = 0;
39 internal static XmlDocument slashdocs;
42 DocumentationEnumerator docEnum;
46 static readonly MemberFormatter csharpFullFormatter = new CSharpFullMemberFormatter ();
47 static readonly MemberFormatter csharpFormatter = new CSharpMemberFormatter ();
48 static readonly MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
49 static readonly MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
51 internal static readonly MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
53 MyXmlNodeList extensionMethods = new MyXmlNodeList ();
55 public override void Run (IEnumerable<string> args)
57 show_exceptions = DebugOutput;
59 var types = new List<string> ();
60 var p = new OptionSet () {
62 "Delete removed members from the XML files.",
63 v => delete = v != null },
65 "Document potential exceptions that members can generate. {SOURCES} " +
66 "is a comma-separated list of:\n" +
67 " asm Method calls in same assembly\n" +
68 " depasm Method calls in dependent assemblies\n" +
69 " all Record all possible exceptions\n" +
70 "If nothing is specified, then only exceptions from the member will " +
72 v => exceptions = ParseExceptionLocations (v) },
74 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
77 case "no-assembly-versions":
78 no_assembly_versions = true;
81 throw new Exception ("Unsupported flag `" + v + "'.");
84 { "fno-assembly-versions",
85 "Do not generate //AssemblyVersion elements.",
86 v => no_assembly_versions = v != null },
88 "Import documentation from {FILE}.",
91 "Check for assembly references in {DIRECTORY}.",
92 v => assemblyResolver.AddSearchDirectory (v) },
94 "Root {DIRECTORY} to generate/update documentation.",
97 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
98 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
99 v => assemblyResolver.AddSearchDirectory (Path.GetDirectoryName (v)) },
101 "Manually specify the assembly {VERSION} that new members were added in.",
104 "Only update documentation for {TYPE}.",
105 v => types.Add (v) },
107 var assemblies = Parse (p, args, "update",
108 "[OPTIONS]+ ASSEMBLIES",
109 "Create or update documentation from ASSEMBLIES.");
110 if (assemblies == null)
112 if (assemblies.Count == 0)
113 Error ("No assemblies specified.");
115 foreach (var dir in assemblies
116 .Where (a => a.Contains (Path.DirectorySeparatorChar))
117 .Select (a => Path.GetDirectoryName (a)))
118 assemblyResolver.AddSearchDirectory (dir);
120 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
123 throw new InvalidOperationException("The --out option is required.");
125 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
127 if (import != null && ecmadocs == null && slashdocs == null) {
129 XmlReader r = new XmlTextReader (import);
131 while (r.NodeType != XmlNodeType.Element) {
133 Error ("Unable to read XML file: {0}.", import);
135 if (r.LocalName == "doc") {
136 var xml = File.ReadAllText (import);
137 // Ensure Unix line endings
138 xml = xml.Replace ("\r", "");
139 slashdocs = new XmlDocument();
140 slashdocs.LoadXml (xml);
142 else if (r.LocalName == "Libraries") {
143 ecmadocs = new XmlTextReader (import);
144 docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
147 Error ("Unsupported XML format within {0}.", import);
150 } catch (Exception e) {
151 Environment.ExitCode = 1;
152 Error ("Could not load XML file: {0}.", e.Message);
156 docEnum = docEnum ?? new DocumentationEnumerator ();
158 // PERFORM THE UPDATES
160 if (types.Count > 0) {
162 DoUpdateTypes (srcPath, types, srcPath);
165 else if (opts.@namespace != null)
166 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
167 Path.Combine (dest_dir, opts.@namespace));
170 DoUpdateAssemblies (srcPath, srcPath);
172 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
175 static ExceptionLocations ParseExceptionLocations (string s)
177 ExceptionLocations loc = ExceptionLocations.Member;
180 foreach (var type in s.Split (',')) {
182 case "added": loc |= ExceptionLocations.AddedMembers; break;
183 case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
184 case "asm": loc |= ExceptionLocations.Assembly; break;
185 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
186 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
192 internal void Warning (string format, params object[] args)
194 Message (TraceLevel.Warning, "mdoc: " + format, args);
197 private AssemblyDefinition LoadAssembly (string name)
199 AssemblyDefinition assembly = null;
201 assembly = AssemblyFactory.GetAssembly (name);
202 } catch (System.IO.FileNotFoundException) { }
204 if (assembly == null)
205 throw new InvalidOperationException("Assembly " + name + " not found.");
207 assembly.Resolver = assemblyResolver;
211 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
212 OrderTypeAttributes (element);
213 XmlTextWriter writer = new XmlTextWriter(output);
214 writer.Formatting = Formatting.Indented;
215 writer.Indentation = 2;
216 writer.IndentChar = ' ';
217 element.WriteTo(writer);
221 private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
223 Action<string> creator = file => {
224 using (var writer = OpenWrite (file, mode))
228 MdocFile.UpdateFile (filename, creator);
231 private static void OrderTypeAttributes (XmlElement e)
233 foreach (XmlElement type in e.SelectNodes ("//Type")) {
234 OrderTypeAttributes (type.Attributes);
238 static readonly string[] TypeAttributeOrder = {
239 "Name", "FullName", "FullNameSP", "Maintainer"
242 private static void OrderTypeAttributes (XmlAttributeCollection c)
244 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
245 for (int i = 0; i < c.Count; ++i) {
246 XmlAttribute a = c [i];
247 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
248 if (a.Name == TypeAttributeOrder [j]) {
254 for (int i = attrs.Length-1; i >= 0; --i) {
255 XmlAttribute n = attrs [i];
258 XmlAttribute r = null;
259 for (int j = i+1; j < attrs.Length; ++j) {
260 if (attrs [j] != null) {
268 c.InsertBefore (n, r);
272 private XmlDocument CreateIndexStub()
274 XmlDocument index = new XmlDocument();
276 XmlElement index_root = index.CreateElement("Overview");
277 index.AppendChild(index_root);
279 if (assemblies.Count == 0)
280 throw new Exception ("No assembly");
282 XmlElement index_assemblies = index.CreateElement("Assemblies");
283 index_root.AppendChild(index_assemblies);
285 XmlElement index_remarks = index.CreateElement("Remarks");
286 index_remarks.InnerText = "To be added.";
287 index_root.AppendChild(index_remarks);
289 XmlElement index_copyright = index.CreateElement("Copyright");
290 index_copyright.InnerText = "To be added.";
291 index_root.AppendChild(index_copyright);
293 XmlElement index_types = index.CreateElement("Types");
294 index_root.AppendChild(index_types);
299 private static void WriteNamespaceStub(string ns, string outdir) {
300 XmlDocument index = new XmlDocument();
302 XmlElement index_root = index.CreateElement("Namespace");
303 index.AppendChild(index_root);
305 index_root.SetAttribute("Name", ns);
307 XmlElement index_docs = index.CreateElement("Docs");
308 index_root.AppendChild(index_docs);
310 XmlElement index_summary = index.CreateElement("summary");
311 index_summary.InnerText = "To be added.";
312 index_docs.AppendChild(index_summary);
314 XmlElement index_remarks = index.CreateElement("remarks");
315 index_remarks.InnerText = "To be added.";
316 index_docs.AppendChild(index_remarks);
318 WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
319 writer => WriteXml (index.DocumentElement, writer));
322 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
324 var found = new HashSet<string> ();
325 foreach (AssemblyDefinition assembly in assemblies) {
326 foreach (DocsTypeInfo docsTypeInfo in docEnum.GetDocumentationTypes (assembly, typenames)) {
327 string relpath = DoUpdateType (docsTypeInfo.Type, basepath, dest, docsTypeInfo.EcmaDocs);
329 found.Add (docsTypeInfo.Type.FullName);
332 var notFound = from n in typenames where !found.Contains (n) select n;
334 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
337 public string DoUpdateType (TypeDefinition type, string basepath, string dest, XmlReader ecmaDocsType)
339 if (type.Namespace == null)
340 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
342 if (!IsPublic (type))
345 // Must get the A+B form of the type name.
346 string typename = GetTypeFileName(type);
348 string reltypefile = DocUtils.PathCombine (DocUtils.GetNamespace (type), typename + ".xml");
349 string typefile = Path.Combine (basepath, reltypefile);
350 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
352 string output = null;
355 } else if (dest == "-") {
358 output = Path.Combine (dest, reltypefile);
363 XmlDocument basefile = new XmlDocument();
365 basefile.Load(typefile);
366 } catch (Exception e) {
367 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
370 DoUpdateType2("Updating", basefile, type, output, false, ecmaDocsType);
373 XmlElement td = StubType(type, output, ecmaDocsType);
377 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
380 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
386 public void DoUpdateNS (string ns, string nspath, string outpath)
388 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
389 AssemblyDefinition assembly = assemblies [0];
391 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
392 XmlDocument basefile = new XmlDocument();
393 string typefile = Path.Combine(nspath, file.Name);
395 basefile.Load(typefile);
396 } catch (Exception e) {
397 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
401 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
402 TypeDefinition type = assembly.GetType(typename);
404 Warning ("Type no longer in assembly: " + typename);
408 seenTypes[type] = seenTypes;
409 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false, null);
412 // Stub types not in the directory
413 foreach (DocsTypeInfo docsTypeInfo in docEnum.GetDocumentationTypes (assembly, null)) {
414 TypeDefinition type = docsTypeInfo.Type;
415 if (type.Namespace != ns || seenTypes.ContainsKey(type))
418 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"), docsTypeInfo.EcmaDocs);
419 if (td == null) continue;
423 private static string GetTypeFileName (TypeReference type)
425 return filenameFormatter.GetName (type);
428 public static string GetTypeFileName (string typename)
430 StringBuilder filename = new StringBuilder (typename.Length);
434 for (int i = 0; i < typename.Length; ++i) {
435 char c = typename [i];
444 filename.Append ('`').Append ((numArgs+1).ToString());
459 return filename.ToString ();
462 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
464 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
465 index_assembly.SetAttribute ("Name", assembly.Name.Name);
466 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
468 AssemblyNameDefinition name = assembly.Name;
469 if (name.HasPublicKey) {
470 XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
471 var key = new StringBuilder (name.PublicKey.Length*3 + 2);
473 foreach (byte b in name.PublicKey)
474 key.AppendFormat ("{0,2:x2} ", b);
476 pubkey.InnerText = key.ToString ();
477 index_assembly.AppendChild (pubkey);
480 if (!string.IsNullOrEmpty (name.Culture)) {
481 XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
482 culture.InnerText = name.Culture;
483 index_assembly.AppendChild (culture);
486 MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
487 parent.AppendChild(index_assembly);
490 private void DoUpdateAssemblies (string source, string dest)
492 string indexfile = dest + "/index.xml";
494 if (System.IO.File.Exists(indexfile)) {
495 index = new XmlDocument();
496 index.Load(indexfile);
499 ClearElement(index.DocumentElement, "Assembly");
500 ClearElement(index.DocumentElement, "Attributes");
502 index = CreateIndexStub();
505 string defaultTitle = "Untitled";
506 if (assemblies.Count == 1)
507 defaultTitle = assemblies[0].Name.Name;
508 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
510 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
511 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
512 index_assemblies.RemoveAll ();
515 HashSet<string> goodfiles = new HashSet<string> ();
517 foreach (AssemblyDefinition assm in assemblies) {
518 AddIndexAssembly (assm, index_assemblies);
519 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
522 SortIndexEntries (index_types);
524 CleanupFiles (dest, goodfiles);
525 CleanupIndexTypes (index_types, goodfiles);
526 CleanupExtensions (index_types);
528 WriteFile (indexfile, FileMode.Create,
529 writer => WriteXml(index.DocumentElement, writer));
532 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
534 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
536 foreach (DocsTypeInfo docTypeInfo in docEnum.GetDocumentationTypes (assembly, null)) {
537 TypeDefinition type = docTypeInfo.Type;
538 string typename = GetTypeFileName(type);
539 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0)
542 string reltypepath = DoUpdateType (type, source, dest, docTypeInfo.EcmaDocs);
543 if (reltypepath == null)
546 // Add namespace and type nodes into the index file as needed
547 string ns = DocUtils.GetNamespace (type);
548 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode("Namespace[@Name='" + ns + "']");
549 if (nsnode == null) {
550 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
551 nsnode.SetAttribute ("Name", ns);
552 index_types.AppendChild(nsnode);
554 string doc_typename = GetDocTypeName (type);
555 XmlElement typenode = (XmlElement)nsnode.SelectSingleNode("Type[@Name='" + typename + "']");
556 if (typenode == null) {
557 typenode = index_types.OwnerDocument.CreateElement("Type");
558 typenode.SetAttribute("Name", typename);
559 nsnode.AppendChild(typenode);
561 if (typename != doc_typename)
562 typenode.SetAttribute("DisplayName", doc_typename);
564 typenode.RemoveAttribute("DisplayName");
565 typenode.SetAttribute ("Kind", GetTypeKind (type));
567 // Ensure the namespace index file exists
568 string onsdoc = DocUtils.PathCombine (dest, type.Namespace + ".xml");
569 string nsdoc = DocUtils.PathCombine (dest, "ns-" + type.Namespace + ".xml");
570 if (File.Exists (onsdoc)) {
571 File.Move (onsdoc, nsdoc);
574 if (!File.Exists (nsdoc)) {
575 Console.WriteLine("New Namespace File: " + type.Namespace);
576 WriteNamespaceStub(type.Namespace, dest);
579 goodfiles.Add (reltypepath);
583 private static void SortIndexEntries (XmlElement indexTypes)
585 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
586 XmlNodeComparer c = new AttributeNameComparer ();
587 SortXmlNodes (indexTypes, namespaces, c);
589 for (int i = 0; i < namespaces.Count; ++i)
590 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
593 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
595 MyXmlNodeList l = new MyXmlNodeList (children.Count);
596 for (int i = 0; i < children.Count; ++i)
597 l.Add (children [i]);
599 for (int i = l.Count - 1; i > 0; --i) {
600 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
604 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
606 public abstract int Compare (XmlNode x, XmlNode y);
608 public int Compare (object x, object y)
610 return Compare ((XmlNode) x, (XmlNode) y);
614 class AttributeNameComparer : XmlNodeComparer {
617 public AttributeNameComparer ()
622 public AttributeNameComparer (string attribute)
624 this.attribute = attribute;
627 public override int Compare (XmlNode x, XmlNode y)
629 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
633 class VersionComparer : XmlNodeComparer {
634 public override int Compare (XmlNode x, XmlNode y)
636 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
637 string a = GetVersion (x.InnerText);
638 string b = GetVersion (y.InnerText);
639 return new Version (a).CompareTo (new Version (b));
642 static string GetVersion (string v)
644 int n = v.IndexOf ("x");
647 return v.Substring (0, n-1);
651 private static string GetTypeKind (TypeDefinition type)
654 return "Enumeration";
655 if (type.IsValueType)
657 if (type.IsInterface)
659 if (DocUtils.IsDelegate (type))
661 if (type.IsClass || type.FullName == "System.Enum") // FIXME
663 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
666 private static bool IsPublic (TypeDefinition type)
668 TypeDefinition decl = type;
669 while (decl != null) {
670 if (!(decl.IsPublic || decl.IsNestedPublic)) {
673 decl = (TypeDefinition) decl.DeclaringType;
678 private void CleanupFiles (string dest, HashSet<string> goodfiles)
680 // Look for files that no longer correspond to types
681 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
682 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
683 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
684 if (!goodfiles.Contains (relTypeFile)) {
685 XmlDocument doc = new XmlDocument ();
686 doc.Load (typefile.FullName);
687 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
688 if (UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
689 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
690 WriteXml(doc.DocumentElement, writer);
691 goodfiles.Add (relTypeFile);
694 string newname = typefile.FullName + ".remove";
695 try { System.IO.File.Delete(newname); } catch (Exception) { }
696 try { typefile.MoveTo(newname); } catch (Exception) { }
697 Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
703 private static TextWriter OpenWrite (string path, FileMode mode)
705 var w = new StreamWriter (
706 new FileStream (path, mode),
707 new UTF8Encoding (false)
713 private string[] GetAssemblyVersions ()
715 return (from a in assemblies select GetAssemblyVersion (a)).ToArray ();
718 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
720 // Look for type nodes that no longer correspond to types
721 MyXmlNodeList remove = new MyXmlNodeList ();
722 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
723 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
724 if (!goodfiles.Contains (fulltypename)) {
725 remove.Add (typenode);
728 foreach (XmlNode n in remove)
729 n.ParentNode.RemoveChild (n);
732 private void CleanupExtensions (XmlElement index_types)
734 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
735 if (extensionMethods.Count == 0) {
738 index_types.SelectSingleNode ("/Overview").RemoveChild (e);
742 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
743 index_types.SelectSingleNode ("/Overview").AppendChild (e);
747 extensionMethods.Sort (DefaultExtensionMethodComparer);
748 foreach (XmlNode m in extensionMethods) {
749 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
753 class ExtensionMethodComparer : XmlNodeComparer {
754 public override int Compare (XmlNode x, XmlNode y)
756 XmlNode xLink = x.SelectSingleNode ("Member/Link");
757 XmlNode yLink = y.SelectSingleNode ("Member/Link");
759 int n = xLink.Attributes ["Type"].Value.CompareTo (
760 yLink.Attributes ["Type"].Value);
763 n = xLink.Attributes ["Member"].Value.CompareTo (
764 yLink.Attributes ["Member"].Value);
765 if (n == 0 && !object.ReferenceEquals (x, y))
766 throw new InvalidOperationException ("Duplicate extension method found!");
771 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
773 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince, XmlReader ecmaDocsType)
775 Console.WriteLine(message + ": " + type.FullName);
777 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
779 // Update type metadata
780 UpdateType(basefile.DocumentElement, type, ecmaDocsType);
782 // Update existing members. Delete member nodes that no longer should be there,
783 // and remember what members are already documented so we don't add them again.
785 MyXmlNodeList todelete = new MyXmlNodeList ();
786 foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
787 XmlElement oldmember = info.Node;
788 IMemberReference oldmember2 = info.Member;
789 string sig = oldmember2 != null ? MakeMemberSignature(oldmember2) : null;
791 // Interface implementations and overrides are deleted from the docs
792 // unless the overrides option is given.
793 if (oldmember2 != null && sig == null)
796 // Deleted (or signature changed)
797 if (oldmember2 == null) {
798 if (UpdateAssemblyVersions (oldmember, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
800 DeleteMember ("Member Removed", output, oldmember, todelete);
805 if (seenmembers.ContainsKey (sig)) {
806 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
807 // ignore, already seen
809 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
810 DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
812 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
816 // Update signature information
819 seenmembers.Add (sig, oldmember);
821 foreach (XmlElement oldmember in todelete)
822 oldmember.ParentNode.RemoveChild (oldmember);
825 if (!DocUtils.IsDelegate (type)) {
826 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
827 foreach (IMemberReference m in type.GetMembers()) {
828 if (m is TypeDefinition) continue;
830 string sig = MakeMemberSignature(m);
831 if (sig == null) continue;
832 if (seenmembers.ContainsKey(sig)) continue;
834 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
835 if (mm == null) continue;
836 members.AppendChild( mm );
838 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
843 // Import code snippets from files
844 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
845 if (!(code is XmlElement)) continue;
846 string file = ((XmlElement)code).GetAttribute("src");
847 string lang = ((XmlElement)code).GetAttribute("lang");
849 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
851 code.InnerText = src;
855 if (insertSince && since != null) {
856 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
857 docs.AppendChild (CreateSinceNode (basefile));
861 XmlElement d = basefile.DocumentElement ["Docs"];
862 XmlElement m = basefile.DocumentElement ["Members"];
863 if (d != null && m != null)
864 basefile.DocumentElement.InsertBefore (
865 basefile.DocumentElement.RemoveChild (d), m);
870 WriteXml(basefile.DocumentElement, Console.Out);
872 FileInfo file = new FileInfo (output);
873 if (!file.Directory.Exists) {
874 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
875 file.Directory.Create ();
877 WriteFile (output, FileMode.Create,
878 writer => WriteXml(basefile.DocumentElement, writer));
882 private string GetCodeSource (string lang, string file)
885 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
886 // Grab the specified region
887 string region = "#region " + file.Substring (anchorStart + 4);
888 file = file.Substring (0, anchorStart + 3);
890 using (StreamReader reader = new StreamReader (file)) {
892 StringBuilder src = new StringBuilder ();
894 while ((line = reader.ReadLine ()) != null) {
895 if (line.Trim() == region) {
896 indent = line.IndexOf (region);
899 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
904 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
907 return src.ToString ();
909 } catch (Exception e) {
910 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
911 file, region, show_exceptions ? e.ToString () : e.Message);
916 using (StreamReader reader = new StreamReader (file))
917 return reader.ReadToEnd ();
918 } catch (Exception e) {
919 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
924 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
926 string format = output != null
927 ? "{0}: File='{1}'; Signature='{4}'"
928 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
932 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
933 member.Attributes ["MemberName"].Value,
934 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
935 if (!delete && MemberDocsHaveUserContent (member)) {
936 Warning ("Member deletions must be enabled with the --delete option.");
938 todelete.Add (member);
943 class MemberComparer : XmlNodeComparer {
944 public override int Compare (XmlNode x, XmlNode y)
947 string xMemberName = x.Attributes ["MemberName"].Value;
948 string yMemberName = y.Attributes ["MemberName"].Value;
950 // generic methods *end* with '>'
951 // it's possible for explicitly implemented generic interfaces to
952 // contain <...> without being a generic method
953 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
954 (r = xMemberName.CompareTo (yMemberName)) != 0)
958 if ((lt = xMemberName.IndexOf ("<")) >= 0)
959 xMemberName = xMemberName.Substring (0, lt);
960 if ((lt = yMemberName.IndexOf ("<")) >= 0)
961 yMemberName = yMemberName.Substring (0, lt);
962 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
965 // if @MemberName matches, then it's either two different types of
966 // members sharing the same name, e.g. field & property, or it's an
967 // overloaded method.
968 // for different type, sort based on MemberType value.
969 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
970 y.SelectSingleNode ("MemberType").InnerText);
974 // same type -- must be an overloaded method. Sort based on type
975 // parameter count, then parameter count, then by the parameter
977 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
978 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
979 if (xTypeParams.Count != yTypeParams.Count)
980 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
981 for (int i = 0; i < xTypeParams.Count; ++i) {
982 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
983 yTypeParams [i].Attributes ["Name"].Value);
988 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
989 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
990 if (xParams.Count != yParams.Count)
991 return xParams.Count <= yParams.Count ? -1 : 1;
992 for (int i = 0; i < xParams.Count; ++i) {
993 r = xParams [i].Attributes ["Type"].Value.CompareTo (
994 yParams [i].Attributes ["Type"].Value);
998 // all parameters match, but return value might not match if it was
999 // changed between one version and another.
1000 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1001 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1002 if (xReturn != null && yReturn != null) {
1003 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1012 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1014 private static void SortTypeMembers (XmlNode members)
1016 if (members == null)
1018 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1021 private static bool MemberDocsHaveUserContent (XmlNode e)
1023 e = (XmlElement)e.SelectSingleNode("Docs");
1024 if (e == null) return false;
1025 foreach (XmlElement d in e.SelectNodes("*"))
1026 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1031 // UPDATE HELPER FUNCTIONS
1033 // CREATE A STUB DOCUMENTATION FILE
1035 public XmlElement StubType (TypeDefinition type, string output, XmlReader ecmaDocsType)
1037 string typesig = MakeTypeSignature(type);
1038 if (typesig == null) return null; // not publicly visible
1040 XmlDocument doc = new XmlDocument();
1041 XmlElement root = doc.CreateElement("Type");
1042 doc.AppendChild (root);
1044 DoUpdateType2 ("New Type", doc, type, output, true, ecmaDocsType);
1049 private XmlElement CreateSinceNode (XmlDocument doc)
1051 XmlElement s = doc.CreateElement ("since");
1052 s.SetAttribute ("version", since);
1056 // STUBBING/UPDATING FUNCTIONS
1058 public void UpdateType (XmlElement root, TypeDefinition type, XmlReader ecmaDocsType)
1060 root.SetAttribute("Name", GetDocTypeName (type));
1061 root.SetAttribute("FullName", GetDocTypeFullName (type));
1063 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Language", "C#");
1064 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Value", MakeTypeSignature(type));
1066 XmlElement ass = WriteElement(root, "AssemblyInfo");
1067 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1068 if (!no_assembly_versions) {
1069 UpdateAssemblyVersions (root, type, true);
1072 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1073 foreach (var version in versions)
1074 ass.RemoveChild (version);
1076 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1077 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1079 ClearElement(ass, "AssemblyCulture");
1081 // Why-oh-why do we put assembly attributes in each type file?
1082 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1083 // since they're outdated in current docs, and a waste of space.
1084 //MakeAttributes(ass, type.Assembly, true);
1085 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1086 if (assattrs != null)
1087 ass.RemoveChild(assattrs);
1089 NormalizeWhitespace(ass);
1091 if (type.IsGenericType ()) {
1092 MakeTypeParameters (root, type.GenericParameters);
1094 ClearElement(root, "TypeParameters");
1097 if (type.BaseType != null) {
1098 XmlElement basenode = WriteElement(root, "Base");
1100 string basetypename = GetDocTypeFullName (type.BaseType);
1101 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1102 WriteElementText(root, "Base/BaseTypeName", basetypename);
1104 // Document how this type instantiates the generic parameters of its base type
1105 TypeReference origBase = type.BaseType.GetOriginalType ();
1106 if (origBase.IsGenericType ()) {
1107 ClearElement(basenode, "BaseTypeArguments");
1108 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1109 GenericArgumentCollection baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1110 GenericParameterCollection baseGenParams = origBase.GenericParameters;
1111 if (baseGenArgs.Count != baseGenParams.Count)
1112 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1113 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1114 GenericParameter param = baseGenParams [i];
1115 TypeReference value = baseGenArgs [i];
1117 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1118 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1119 bta.AppendChild(arg);
1120 arg.SetAttribute ("TypeParamName", param.Name);
1121 arg.InnerText = GetDocTypeFullName (value);
1125 ClearElement(root, "Base");
1128 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1129 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1130 List<string> interface_names = userInterfaces
1131 .Select (iface => GetDocTypeFullName (iface))
1135 XmlElement interfaces = WriteElement(root, "Interfaces");
1136 interfaces.RemoveAll();
1137 foreach (string iname in interface_names) {
1138 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1139 interfaces.AppendChild(iface);
1140 WriteElementText(iface, "InterfaceName", iname);
1143 ClearElement(root, "Interfaces");
1146 MakeAttributes (root, GetCustomAttributes (type));
1148 if (DocUtils.IsDelegate (type)) {
1149 MakeTypeParameters (root, type.GenericParameters);
1150 MakeParameters(root, type.GetMethod("Invoke").Parameters);
1151 MakeReturnValue(root, type.GetMethod("Invoke"));
1154 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1155 if (ecmaDocsType != null) {
1156 if (ecmaDocsType.Name != "Docs") {
1157 int depth = ecmaDocsType.Depth;
1158 while (ecmaDocsType.Read ()) {
1159 if (ecmaDocsType.Name == "Docs" && ecmaDocsType.Depth == depth + 1)
1163 if (!ecmaDocsType.IsStartElement ("Docs"))
1164 throw new InvalidOperationException ("Found " + ecmaDocsType.Name + "; expecting <Docs/>!");
1165 typeInfo.EcmaDocs = ecmaDocsType;
1167 MakeDocNode (typeInfo);
1169 if (!DocUtils.IsDelegate (type))
1170 WriteElement (root, "Members");
1172 NormalizeWhitespace(root);
1175 internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1177 List<T> l = new List<T> (list);
1182 private void UpdateMember (DocsNodeInfo info)
1184 XmlElement me = (XmlElement) info.Node;
1185 IMemberReference mi = info.Member;
1186 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Language", "C#");
1187 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Value", MakeMemberSignature(mi));
1189 WriteElementText(me, "MemberType", GetMemberType(mi));
1191 if (!no_assembly_versions) {
1192 UpdateAssemblyVersions (me, mi, true);
1195 ClearElement (me, "AssemblyInfo");
1198 MakeAttributes (me, GetCustomAttributes (mi));
1200 MakeReturnValue(me, mi);
1201 if (mi is MethodReference) {
1202 MethodReference mb = (MethodReference) mi;
1203 if (mb.IsGenericMethod ())
1204 MakeTypeParameters (me, mb.GenericParameters);
1206 MakeParameters(me, mi);
1209 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1210 WriteElementText(me, "MemberValue", fieldValue);
1212 info.Node = WriteElement (me, "Docs");
1214 UpdateExtensionMethods (me, info);
1217 IEnumerable<string> GetCustomAttributes (IMemberReference mi)
1219 IEnumerable<string> attrs = Enumerable.Empty<string>();
1221 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1223 attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1225 PropertyReference pr = mi as PropertyReference;
1227 PropertyDefinition pd = pr.Resolve ();
1228 if (pd.GetMethod != null)
1229 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1230 if (pd.SetMethod != null)
1231 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1234 EventReference er = mi as EventReference;
1236 EventDefinition ed = er.Resolve ();
1237 if (ed.AddMethod != null)
1238 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1239 if (ed.RemoveMethod != null)
1240 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1246 IEnumerable<string> GetCustomAttributes (CustomAttributeCollection attributes, string prefix)
1248 foreach (CustomAttribute attribute in attributes.Cast<CustomAttribute> ()
1249 .OrderBy (ca => ca.Constructor.DeclaringType.FullName)) {
1250 if (!attribute.Resolve ()) {
1252 Warning ("warning: could not resolve type {0}.",
1253 attribute.Constructor.DeclaringType.FullName);
1255 TypeDefinition attrType = attribute.Constructor.DeclaringType as TypeDefinition;
1256 if (attrType != null && !IsPublic (attrType))
1258 if (slashdocFormatter.GetName (attribute.Constructor.DeclaringType) == null)
1261 if (Array.IndexOf (IgnorableAttributes, attribute.Constructor.DeclaringType.FullName) >= 0)
1264 StringList fields = new StringList ();
1266 ParameterDefinitionCollection parameters = attribute.Constructor.Parameters;
1267 for (int i = 0; i < attribute.ConstructorParameters.Count; ++i) {
1268 fields.Add (MakeAttributesValueString (
1269 attribute.ConstructorParameters [i],
1270 parameters [i].ParameterType));
1273 (from de in attribute.Fields.Cast<DictionaryEntry> ()
1274 select new { Type=attribute.GetFieldType (de.Key.ToString ()), Name=de.Key, Value=de.Value })
1276 (from de in attribute.Properties.Cast<DictionaryEntry> ()
1277 select new { Type=attribute.GetPropertyType (de.Key.ToString ()), Name=de.Key, Value=de.Value }))
1278 .OrderBy (v => v.Name);
1279 foreach (var d in namedArgs)
1280 fields.Add (string.Format ("{0}={1}", d.Name,
1281 MakeAttributesValueString (d.Value, d.Type)));
1283 string a2 = String.Join(", ", fields.ToArray ());
1284 if (a2 != "") a2 = "(" + a2 + ")";
1286 string name = attribute.Constructor.DeclaringType.FullName;
1287 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1288 yield return prefix + name + a2;
1292 static readonly string[] ValidExtensionMembers = {
1301 static readonly string[] ValidExtensionDocMembers = {
1307 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1309 MethodDefinition me = info.Member as MethodDefinition;
1312 if (info.Parameters.Count < 1)
1314 if (!DocUtils.IsExtensionMethod (me))
1317 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1318 XmlNode member = e.CloneNode (true);
1319 em.AppendChild (member);
1320 RemoveExcept (member, ValidExtensionMembers);
1321 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1322 WriteElementText (member, "MemberType", "ExtensionMethod");
1323 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1324 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1325 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1326 member.AppendChild (link);
1327 AddTargets (em, info);
1329 extensionMethods.Add (em);
1332 private static void RemoveExcept (XmlNode node, string[] except)
1336 MyXmlNodeList remove = null;
1337 foreach (XmlNode n in node.ChildNodes) {
1338 if (Array.BinarySearch (except, n.Name) < 0) {
1340 remove = new MyXmlNodeList ();
1345 foreach (XmlNode n in remove)
1346 node.RemoveChild (n);
1349 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1351 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1352 member.PrependChild (targets);
1353 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1354 AppendElementAttributeText (targets, "Target", "Type",
1355 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1358 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1359 ConstraintCollection constraints = gp.Constraints;
1360 if (constraints.Count == 0)
1361 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1363 foreach (TypeReference c in constraints)
1364 AppendElementAttributeText(targets, "Target", "Type",
1365 slashdocFormatter.GetDeclaration (c));
1369 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1372 TypeDefinition type = field.DeclaringType.Resolve ();
1373 if (type != null && type.IsEnum) return false;
1375 if (type != null && type.IsGenericType ()) return false;
1376 if (!field.HasConstant)
1378 if (field.IsLiteral) {
1379 object val = field.Constant;
1380 if (val == null) value = "null";
1381 else if (val is Enum) value = val.ToString();
1382 else if (val is IFormattable) {
1383 value = ((IFormattable)val).ToString();
1385 value = "\"" + value + "\"";
1387 if (value != null && value != "")
1393 // XML HELPER FUNCTIONS
1395 internal static XmlElement WriteElement(XmlNode parent, string element) {
1396 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1398 string[] path = element.Split('/');
1399 foreach (string p in path) {
1400 ret = (XmlElement)parent.SelectSingleNode(p);
1403 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1404 ename = ename.Substring(0, ename.IndexOf('['));
1405 ret = parent.OwnerDocument.CreateElement(ename);
1406 parent.AppendChild(ret);
1415 private static void WriteElementText(XmlNode parent, string element, string value) {
1416 XmlElement node = WriteElement(parent, element);
1417 node.InnerText = value;
1420 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1422 XmlElement n = parent.OwnerDocument.CreateElement (element);
1423 parent.AppendChild (n);
1424 n.InnerText = value;
1428 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1430 XmlElement n = parent.OwnerDocument.CreateElement (element);
1431 parent.AppendChild (n);
1432 n.SetAttribute (attribute, value);
1436 private static XmlNode CopyNode (XmlNode source, XmlNode dest)
1438 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1439 dest.AppendChild (copy);
1443 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1444 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1447 node = WriteElement(parent, element);
1448 node.InnerText = value;
1450 private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
1451 XmlElement node = WriteElement(parent, element);
1452 if (node.GetAttribute(attribute) == value) return;
1453 node.SetAttribute(attribute, value);
1455 private static void ClearElement(XmlElement parent, string name) {
1456 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1458 parent.RemoveChild(node);
1461 // DOCUMENTATION HELPER FUNCTIONS
1463 private void MakeDocNode (DocsNodeInfo info)
1465 List<GenericParameter> genericParams = info.GenericParameters;
1466 ParameterDefinitionCollection parameters = info.Parameters;
1467 TypeReference returntype = info.ReturnType;
1468 bool returnisreturn = info.ReturnIsReturn;
1469 XmlElement e = info.Node;
1470 bool addremarks = info.AddRemarks;
1472 WriteElementInitialText(e, "summary", "To be added.");
1474 if (parameters != null) {
1475 string[] values = new string [parameters.Count];
1476 for (int i = 0; i < values.Length; ++i)
1477 values [i] = parameters [i].Name;
1478 UpdateParameters (e, "param", values);
1481 if (genericParams != null) {
1482 string[] values = new string [genericParams.Count];
1483 for (int i = 0; i < values.Length; ++i)
1484 values [i] = genericParams [i].Name;
1485 UpdateParameters (e, "typeparam", values);
1488 string retnodename = null;
1489 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
1490 retnodename = returnisreturn ? "returns" : "value";
1491 string retnodename_other = !returnisreturn ? "returns" : "value";
1493 // If it has a returns node instead of a value node, change its name.
1494 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1495 if (retother != null) {
1496 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1497 foreach (XmlNode node in retother)
1498 retnode.AppendChild(node.CloneNode(true));
1499 e.ReplaceChild(retnode, retother);
1501 WriteElementInitialText(e, retnodename, "To be added.");
1504 ClearElement(e, "returns");
1505 ClearElement(e, "value");
1509 WriteElementInitialText(e, "remarks", "To be added.");
1511 if (exceptions.HasValue && info.Member != null &&
1512 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
1513 UpdateExceptions (e, info.Member);
1516 if (info.EcmaDocs != null) {
1517 XmlReader r = info.EcmaDocs;
1518 int depth = r.Depth;
1519 r.ReadStartElement ("Docs");
1521 if (r.Name == "Docs") {
1522 if (r.Depth == depth && r.NodeType == XmlNodeType.EndElement)
1525 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
1527 if (!r.IsStartElement ())
1532 string name = r.GetAttribute ("name");
1535 XmlNode doc = e.SelectSingleNode (
1536 r.Name + "[@name='" + name + "']");
1537 string value = r.ReadInnerXml ();
1539 doc.InnerXml = value.Replace ("\r", "");
1546 string name = r.Name;
1547 string cref = r.GetAttribute ("cref");
1550 XmlNode doc = e.SelectSingleNode (
1551 r.Name + "[@cref='" + cref + "']");
1552 string value = r.ReadInnerXml ().Replace ("\r", "");
1554 doc.InnerXml = value;
1556 XmlElement n = e.OwnerDocument.CreateElement (name);
1557 n.SetAttribute ("cref", cref);
1564 string name = r.Name;
1565 string xpath = r.Name;
1566 StringList attributes = new StringList (r.AttributeCount);
1567 if (r.MoveToFirstAttribute ()) {
1569 attributes.Add ("@" + r.Name + "=\"" + r.Value + "\"");
1570 } while (r.MoveToNextAttribute ());
1573 if (attributes.Count > 0) {
1574 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
1576 XmlNode doc = e.SelectSingleNode (xpath);
1577 string value = r.ReadInnerXml ().Replace ("\r", "");
1579 doc.InnerXml = value;
1582 XmlElement n = e.OwnerDocument.CreateElement (name);
1584 foreach (string a in attributes) {
1585 int eq = a.IndexOf ('=');
1586 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
1595 if (info.SlashDocs != null) {
1596 XmlNode elem = info.SlashDocs;
1598 if (elem.SelectSingleNode("summary") != null)
1599 ClearElement(e, "summary");
1600 if (elem.SelectSingleNode("remarks") != null)
1601 ClearElement(e, "remarks");
1602 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
1603 ClearElement(e, "value");
1604 ClearElement(e, "returns");
1607 foreach (XmlNode child in elem.ChildNodes) {
1608 switch (child.Name) {
1611 XmlAttribute name = child.Attributes ["name"];
1614 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
1616 p2.InnerXml = child.InnerXml;
1619 // Occasionally XML documentation will use <returns/> on
1620 // properties, so let's try to normalize things.
1623 XmlElement v = e.OwnerDocument.CreateElement (retnodename ?? child.Name);
1624 v.InnerXml = child.InnerXml;
1630 case "permission": {
1631 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
1634 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
1636 a = e.OwnerDocument.CreateElement (child.Name);
1637 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
1640 a.InnerXml = child.InnerXml;
1644 XmlAttribute cref = child.Attributes ["cref"];
1647 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
1649 a = e.OwnerDocument.CreateElement ("altmember");
1650 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
1657 foreach (XmlElement n in e.SelectNodes (child.Name))
1658 if (n.OuterXml == child.OuterXml) {
1663 CopyNode (child, e);
1672 OrderDocsNodes (e, e.ChildNodes);
1673 NormalizeWhitespace(e);
1676 static readonly string[] DocsNodeOrder = {
1677 "typeparam", "param", "summary", "returns", "value", "remarks",
1680 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
1682 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1683 for (int i = 0; i < DocsNodeOrder.Length; ++i) {
1684 for (int j = 0; j < children.Count; ++j) {
1685 XmlNode c = children [j];
1686 if (c.Name == DocsNodeOrder [i]) {
1687 newChildren.Add (c);
1691 if (newChildren.Count >= 0)
1692 docs.PrependChild ((XmlNode) newChildren [0]);
1693 for (int i = 1; i < newChildren.Count; ++i) {
1694 XmlNode prev = (XmlNode) newChildren [i-1];
1695 XmlNode cur = (XmlNode) newChildren [i];
1696 docs.RemoveChild (cur);
1697 docs.InsertAfter (cur, prev);
1702 private void UpdateParameters (XmlElement e, string element, string[] values)
1704 if (values != null) {
1705 XmlNode[] paramnodes = new XmlNode[values.Length];
1707 // Some documentation had param nodes with leading spaces.
1708 foreach (XmlElement paramnode in e.SelectNodes(element)){
1709 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
1712 // If a member has only one parameter, we can track changes to
1713 // the name of the parameter easily.
1714 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
1715 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
1718 bool reinsert = false;
1720 // Pick out existing and still-valid param nodes, and
1721 // create nodes for parameters not in the file.
1722 Hashtable seenParams = new Hashtable();
1723 for (int pi = 0; pi < values.Length; pi++) {
1724 string p = values [pi];
1727 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
1728 if (paramnodes[pi] != null) continue;
1730 XmlElement pe = e.OwnerDocument.CreateElement(element);
1731 pe.SetAttribute("name", p);
1732 pe.InnerText = "To be added.";
1733 paramnodes[pi] = pe;
1737 // Remove parameters that no longer exist and check all params are in the right order.
1739 MyXmlNodeList todelete = new MyXmlNodeList ();
1740 foreach (XmlElement paramnode in e.SelectNodes(element)) {
1741 string name = paramnode.GetAttribute("name");
1742 if (!seenParams.ContainsKey(name)) {
1743 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1744 Warning ("The following param node can only be deleted if the --delete option is given: ");
1745 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
1747 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
1748 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1752 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
1753 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1754 e.ParentNode.Attributes ["MemberName"].Value,
1757 Warning ("\tValue={0}", paramnode.OuterXml);
1759 todelete.Add (paramnode);
1764 if ((int)seenParams[name] != idx)
1770 foreach (XmlNode n in todelete) {
1771 n.ParentNode.RemoveChild (n);
1774 // Re-insert the parameter nodes at the top of the doc section.
1776 for (int pi = values.Length-1; pi >= 0; pi--)
1777 e.PrependChild(paramnodes[pi]);
1779 // Clear all existing param nodes
1780 foreach (XmlNode paramnode in e.SelectNodes(element)) {
1781 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1782 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
1783 Console.WriteLine(paramnode.OuterXml);
1785 paramnode.ParentNode.RemoveChild(paramnode);
1791 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
1793 string existingName = pe.GetAttribute ("name");
1794 pe.SetAttribute ("name", newName);
1795 if (existingName == newName)
1797 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
1798 if (paramref.GetAttribute ("name").Trim () == existingName)
1799 paramref.SetAttribute ("name", newName);
1802 class CrefComparer : XmlNodeComparer {
1804 public CrefComparer ()
1808 public override int Compare (XmlNode x, XmlNode y)
1810 string xType = x.Attributes ["cref"].Value;
1811 string yType = y.Attributes ["cref"].Value;
1812 string xNamespace = GetNamespace (xType);
1813 string yNamespace = GetNamespace (yType);
1815 int c = xNamespace.CompareTo (yNamespace);
1818 return xType.CompareTo (yType);
1821 static string GetNamespace (string type)
1823 int n = type.LastIndexOf ('.');
1825 return type.Substring (0, n);
1826 return string.Empty;
1830 private void UpdateExceptions (XmlNode docs, IMemberReference member)
1832 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
1833 string cref = slashdocFormatter.GetDeclaration (source.Exception);
1834 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
1837 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
1838 e.SetAttribute ("cref", cref);
1839 e.InnerXml = "To be added; from: <see cref=\"" +
1840 string.Join ("\" />, <see cref=\"",
1841 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
1844 docs.AppendChild (e);
1846 SortXmlNodes (docs, docs.SelectNodes ("exception"),
1847 new CrefComparer ());
1850 private static void NormalizeWhitespace(XmlElement e) {
1851 // Remove all text and whitespace nodes from the element so it
1852 // is outputted with nice indentation and no blank lines.
1853 ArrayList deleteNodes = new ArrayList();
1854 foreach (XmlNode n in e)
1855 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
1857 foreach (XmlNode n in deleteNodes)
1858 n.ParentNode.RemoveChild(n);
1861 private static bool UpdateAssemblyVersions (XmlElement root, IMemberReference member, bool add)
1863 TypeDefinition type = member as TypeDefinition;
1865 type = member.DeclaringType as TypeDefinition;
1866 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
1869 private static string GetAssemblyVersion (AssemblyDefinition assembly)
1871 return assembly.Name.Version.ToString();
1874 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
1876 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
1878 e = root.OwnerDocument.CreateElement("AssemblyInfo");
1879 root.AppendChild(e);
1881 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
1882 .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
1884 // matches.Count > 0 && add: ignore -- already present
1885 if (matches.Count > 0 && !add) {
1886 foreach (XmlNode c in matches)
1889 else if (matches.Count == 0 && add) {
1890 foreach (string sv in assemblyVersions) {
1891 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
1896 // matches.Count == 0 && !add: ignore -- already not present
1898 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
1899 SortXmlNodes (e, avs, new VersionComparer ());
1901 return avs.Count != 0;
1904 // FIXME: get TypeReferences instead of string comparison?
1905 private static string[] IgnorableAttributes = {
1906 // Security related attributes
1907 "System.Reflection.AssemblyKeyFileAttribute",
1908 "System.Reflection.AssemblyDelaySignAttribute",
1909 // Present in @RefType
1910 "System.Runtime.InteropServices.OutAttribute",
1911 // For naming the indexer to use when not using indexers
1912 "System.Reflection.DefaultMemberAttribute",
1913 // for decimal constants
1914 "System.Runtime.CompilerServices.DecimalConstantAttribute",
1915 // compiler generated code
1916 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
1917 // more compiler generated code, e.g. iterator methods
1918 "System.Diagnostics.DebuggerHiddenAttribute",
1919 "System.Runtime.CompilerServices.FixedBufferAttribute",
1920 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
1921 // extension methods
1922 "System.Runtime.CompilerServices.ExtensionAttribute",
1925 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes)
1927 if (!attributes.Any ()) {
1928 ClearElement (root, "Attributes");
1932 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
1936 e = root.OwnerDocument.CreateElement("Attributes");
1938 foreach (string attribute in attributes) {
1939 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
1942 WriteElementText(ae, "AttributeName", attribute);
1945 if (e.ParentNode == null)
1946 root.AppendChild(e);
1948 NormalizeWhitespace(e);
1951 private static string MakeAttributesValueString (object v, TypeReference valueType)
1955 if (valueType.FullName == "System.Type")
1956 return "typeof(" + v.ToString () + ")";
1957 if (valueType.FullName == "System.String")
1958 return "\"" + v.ToString () + "\"";
1960 return (bool)v ? "true" : "false";
1961 TypeDefinition valueDef = valueType.Resolve ();
1962 if (valueDef == null || !valueDef.IsEnum)
1963 return v.ToString ();
1964 string typename = GetDocTypeFullName (valueType);
1965 var values = GetEnumerationValues (valueDef);
1966 long c = ToInt64 (v);
1967 if (values.ContainsKey (c))
1968 return typename + "." + values [c];
1969 if (valueDef.CustomAttributes.Cast<CustomAttribute> ()
1970 .Any (ca => ca.Constructor.DeclaringType.FullName == "System.FlagsAttribute")) {
1971 return string.Join (" | ",
1972 (from i in values.Keys
1974 select typename + "." + values [i])
1977 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
1980 private static Dictionary<long, string> GetEnumerationValues (TypeDefinition type)
1982 var values = new Dictionary<long, string> ();
1984 (from f in type.Fields.Cast<FieldDefinition> ()
1985 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
1987 values [ToInt64 (f.Constant)] = f.Name;
1992 static long ToInt64 (object value)
1995 return (long) (ulong) value;
1996 return Convert.ToInt64 (value);
1999 private void MakeParameters (XmlElement root, ParameterDefinitionCollection parameters)
2001 XmlElement e = WriteElement(root, "Parameters");
2003 foreach (ParameterDefinition p in parameters) {
2004 XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
2006 pe.SetAttribute("Name", p.Name);
2007 pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
2008 if (p.ParameterType is ReferenceType) {
2009 if (p.IsOut) pe.SetAttribute("RefType", "out");
2010 else pe.SetAttribute("RefType", "ref");
2012 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
2016 private void MakeTypeParameters (XmlElement root, GenericParameterCollection typeParams)
2018 if (typeParams == null || typeParams.Count == 0) {
2019 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2021 root.RemoveChild (f);
2024 XmlElement e = WriteElement(root, "TypeParameters");
2026 foreach (GenericParameter t in typeParams) {
2027 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2029 pe.SetAttribute("Name", t.Name);
2030 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""));
2031 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2032 ConstraintCollection constraints = t.Constraints;
2033 GenericParameterAttributes attrs = t.Attributes;
2034 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2042 ce = root.OwnerDocument.CreateElement ("Constraints");
2044 pe.AppendChild (ce);
2045 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2046 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2047 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2048 AppendElementText (ce, "ParameterAttribute", "Covariant");
2049 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2050 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2051 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2052 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2053 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2054 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2055 foreach (TypeReference c in constraints) {
2056 TypeDefinition cd = c.Resolve ();
2057 AppendElementText (ce,
2058 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2059 GetDocTypeFullName (c));
2064 private void MakeParameters (XmlElement root, IMemberReference mi)
2066 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2067 MakeParameters (root, ((MethodDefinition)mi).Parameters);
2068 else if (mi is MethodDefinition) {
2069 MethodDefinition mb = (MethodDefinition) mi;
2070 ParameterDefinitionCollection parameters = mb.Parameters;
2071 MakeParameters(root, parameters);
2072 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2073 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2074 p.SetAttribute ("RefType", "this");
2077 else if (mi is PropertyDefinition) {
2078 ParameterDefinitionCollection parameters = ((PropertyDefinition)mi).Parameters;
2079 if (parameters.Count > 0)
2080 MakeParameters(root, parameters);
2084 else if (mi is FieldDefinition) return;
2085 else if (mi is EventDefinition) return;
2086 else throw new ArgumentException();
2089 internal static string GetDocParameterType (TypeReference type)
2091 return GetDocTypeFullName (type).Replace ("@", "&");
2094 private void MakeReturnValue (XmlElement root, TypeReference type, CustomAttributeCollection attributes)
2096 XmlElement e = WriteElement(root, "ReturnValue");
2098 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2099 if (attributes != null)
2100 MakeAttributes(e, GetCustomAttributes (attributes, ""));
2103 private void MakeReturnValue (XmlElement root, IMemberReference mi)
2105 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2107 else if (mi is MethodDefinition)
2108 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType.ReturnType, ((MethodDefinition)mi).ReturnType.CustomAttributes);
2109 else if (mi is PropertyDefinition)
2110 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
2111 else if (mi is FieldDefinition)
2112 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
2113 else if (mi is EventDefinition)
2114 MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
2116 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2119 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2121 IMemberReference mi = info.Member;
2122 if (mi is TypeDefinition) return null;
2124 string sigs = MakeMemberSignature(mi);
2125 if (sigs == null) return null; // not publicly visible
2127 // no documentation for property/event accessors. Is there a better way of doing this?
2128 if (mi.Name.StartsWith("get_")) return null;
2129 if (mi.Name.StartsWith("set_")) return null;
2130 if (mi.Name.StartsWith("add_")) return null;
2131 if (mi.Name.StartsWith("remove_")) return null;
2132 if (mi.Name.StartsWith("raise_")) return null;
2134 XmlElement me = doc.CreateElement("Member");
2135 me.SetAttribute("MemberName", GetMemberName (mi));
2139 if (exceptions.HasValue &&
2140 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2141 UpdateExceptions (info.Node, info.Member);
2143 if (since != null) {
2144 XmlNode docs = me.SelectSingleNode("Docs");
2145 docs.AppendChild (CreateSinceNode (doc));
2151 internal static string GetMemberName (IMemberReference mi)
2153 MethodDefinition mb = mi as MethodDefinition;
2155 PropertyDefinition pi = mi as PropertyDefinition;
2158 return DocUtils.GetPropertyName (pi);
2160 StringBuilder sb = new StringBuilder (mi.Name.Length);
2161 if (!DocUtils.IsExplicitlyImplemented (mb))
2162 sb.Append (mi.Name);
2164 TypeReference iface;
2165 MethodReference ifaceMethod;
2166 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2167 sb.Append (GetDocTypeFullName (iface));
2169 sb.Append (ifaceMethod.Name);
2171 if (mb.IsGenericMethod ()) {
2172 GenericParameterCollection typeParams = mb.GenericParameters;
2173 if (typeParams.Count > 0) {
2175 sb.Append (typeParams [0].Name);
2176 for (int i = 1; i < typeParams.Count; ++i)
2177 sb.Append (",").Append (typeParams [i].Name);
2181 return sb.ToString ();
2184 /// SIGNATURE GENERATION FUNCTIONS
2186 static string MakeTypeSignature (TypeReference type)
2188 return csharpFormatter.GetDeclaration (type);
2191 internal static string MakeMemberSignature (IMemberReference mi)
2193 return csharpFullFormatter.GetDeclaration (mi);
2196 internal static string GetMemberType (IMemberReference mi)
2198 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2199 return "Constructor";
2200 if (mi is MethodDefinition)
2202 if (mi is PropertyDefinition)
2204 if (mi is FieldDefinition)
2206 if (mi is EventDefinition)
2208 throw new ArgumentException();
2211 private static string GetDocTypeName (TypeReference type)
2213 return docTypeFormatter.GetName (type);
2216 internal static string GetDocTypeFullName (TypeReference type)
2218 return DocTypeFullMemberFormatter.Default.GetName (type);
2221 internal static string GetXPathForMember (DocumentationMember member)
2223 StringBuilder xpath = new StringBuilder ();
2224 xpath.Append ("//Members/Member[@MemberName=\"")
2225 .Append (member.MemberName)
2227 if (member.Parameters != null && member.Parameters.Count > 0) {
2228 xpath.Append ("/Parameters[count(Parameter) = ")
2229 .Append (member.Parameters.Count);
2230 for (int i = 0; i < member.Parameters.Count; ++i) {
2231 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2232 xpath.Append (member.Parameters [i]);
2233 xpath.Append ("\"");
2235 xpath.Append ("]/..");
2237 return xpath.ToString ();
2240 public static string GetXPathForMember (XPathNavigator member)
2242 StringBuilder xpath = new StringBuilder ();
2243 xpath.Append ("//Type[@FullName=\"")
2244 .Append (member.SelectSingleNode ("../../@FullName").Value)
2246 xpath.Append ("Members/Member[@MemberName=\"")
2247 .Append (member.SelectSingleNode ("@MemberName").Value)
2249 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2250 if (parameters.Count > 0) {
2251 xpath.Append ("/Parameters[count(Parameter) = ")
2252 .Append (parameters.Count);
2254 while (parameters.MoveNext ()) {
2256 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2257 xpath.Append (parameters.Current.Value);
2258 xpath.Append ("\"");
2260 xpath.Append ("]/..");
2262 return xpath.ToString ();
2265 public static string GetXPathForMember (IMemberReference member)
2267 StringBuilder xpath = new StringBuilder ();
2268 xpath.Append ("//Type[@FullName=\"")
2269 .Append (member.DeclaringType.FullName)
2271 xpath.Append ("Members/Member[@MemberName=\"")
2272 .Append (GetMemberName (member))
2275 ParameterDefinitionCollection parameters = null;
2276 if (member is MethodDefinition)
2277 parameters = ((MethodDefinition) member).Parameters;
2278 else if (member is PropertyDefinition) {
2279 parameters = ((PropertyDefinition) member).Parameters;
2281 if (parameters != null && parameters.Count > 0) {
2282 xpath.Append ("/Parameters[count(Parameter) = ")
2283 .Append (parameters.Count);
2284 for (int i = 0; i < parameters.Count; ++i) {
2285 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2286 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2287 xpath.Append ("\"");
2289 xpath.Append ("]/..");
2291 return xpath.ToString ();
2295 static class CecilExtensions {
2296 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type)
2298 foreach (var c in type.Constructors)
2299 yield return (IMemberReference) c;
2300 foreach (var e in type.Events)
2301 yield return (IMemberReference) e;
2302 foreach (var f in type.Fields)
2303 yield return (IMemberReference) f;
2304 foreach (var m in type.Methods)
2305 yield return (IMemberReference) m;
2306 foreach (var t in type.NestedTypes)
2307 yield return (IMemberReference) t;
2308 foreach (var p in type.Properties)
2309 yield return (IMemberReference) p;
2312 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type, string member)
2314 return GetMembers (type).Where (m => m.Name == member);
2317 public static IMemberReference GetMember (this TypeDefinition type, string member)
2319 return GetMembers (type, member).EnsureZeroOrOne ();
2322 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2324 if (source.Count () > 1)
2325 throw new InvalidOperationException ("too many matches");
2326 return source.FirstOrDefault ();
2329 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2331 return type.Methods.Cast<MethodDefinition> ()
2332 .Where (m => m.Name == method)
2333 .EnsureZeroOrOne ();
2336 public static IEnumerable<IMemberReference> GetDefaultMembers (this TypeReference type)
2338 TypeDefinition def = type as TypeDefinition;
2340 return new IMemberReference [0];
2341 CustomAttribute defMemberAttr = type.CustomAttributes.Cast<CustomAttribute> ()
2342 .Where (c => c.Constructor.DeclaringType.FullName == "System.Reflection.DefaultMemberAttribute")
2344 if (defMemberAttr == null)
2345 return new IMemberReference [0];
2346 string name = (string) defMemberAttr.ConstructorParameters [0];
2347 return def.Properties.Cast<PropertyDefinition> ()
2348 .Where (p => p.Name == name)
2349 .Select (p => (IMemberReference) p);
2352 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2354 return assembly.Modules.Cast<ModuleDefinition> ()
2355 .SelectMany (md => md.Types.Cast<TypeDefinition> ());
2358 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2360 return GetTypes (assembly)
2361 .Where (td => td.FullName == type)
2362 .EnsureZeroOrOne ();
2365 public static bool IsGenericType (this TypeReference type)
2367 return type.GenericParameters.Count > 0;
2370 public static bool IsGenericMethod (this MethodReference method)
2372 return method.GenericParameters.Count > 0;
2375 public static IMemberReference Resolve (this IMemberReference member)
2377 EventReference er = member as EventReference;
2379 return er.Resolve ();
2380 FieldReference fr = member as FieldReference;
2382 return fr.Resolve ();
2383 MethodReference mr = member as MethodReference;
2385 return mr.Resolve ();
2386 PropertyReference pr = member as PropertyReference;
2388 return pr.Resolve ();
2389 TypeReference tr = member as TypeReference;
2391 return tr.Resolve ();
2392 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2396 static class DocUtils {
2397 public static bool IsExplicitlyImplemented (MethodDefinition method)
2399 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2402 public static string GetTypeDotMember (string name)
2404 int startType, startMethod;
2405 startType = startMethod = -1;
2406 for (int i = 0; i < name.Length; ++i) {
2407 if (name [i] == '.') {
2408 startType = startMethod;
2412 return name.Substring (startType+1);
2415 public static string GetMember (string name)
2417 int i = name.LastIndexOf ('.');
2420 return name.Substring (i+1);
2423 public static void GetInfoForExplicitlyImplementedMethod (
2424 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2428 if (method.Overrides.Count != 1)
2429 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2430 iface = method.Overrides [0].DeclaringType;
2431 ifaceMethod = method.Overrides [0];
2434 public static string GetPropertyName (PropertyDefinition pi)
2436 // Issue: (g)mcs-generated assemblies that explicitly implement
2437 // properties don't specify the full namespace, just the
2438 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2439 MethodDefinition method = pi.GetMethod;
2441 method = pi.SetMethod;
2442 if (!IsExplicitlyImplemented (method))
2445 // Need to determine appropriate namespace for this member.
2446 TypeReference iface;
2447 MethodReference ifaceMethod;
2448 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2449 return string.Join (".", new string[]{
2450 DocTypeFullMemberFormatter.Default.GetName (iface),
2451 GetMember (pi.Name)});
2454 public static string GetNamespace (TypeReference type)
2456 if (type.GetOriginalType ().IsNested)
2457 type = type.GetOriginalType ();
2458 while (type != null && type.IsNested)
2459 type = type.DeclaringType;
2461 return string.Empty;
2462 return type.Namespace;
2465 public static string PathCombine (string dir, string path)
2471 return Path.Combine (dir, path);
2474 public static bool IsExtensionMethod (MethodDefinition method)
2477 method.CustomAttributes.Cast<CustomAttribute> ()
2478 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2480 method.DeclaringType.CustomAttributes.Cast<CustomAttribute> ()
2481 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2485 public static bool IsDelegate (TypeDefinition type)
2487 TypeReference baseRef = type.BaseType;
2488 if (baseRef == null)
2490 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2491 baseRef.FullName == "System.MulticastDelegate";
2494 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2496 List<TypeReference> decls = new List<TypeReference> ();
2498 while (type.DeclaringType != null) {
2499 decls.Add (type.DeclaringType);
2500 type = type.DeclaringType;
2506 public static int GetGenericArgumentCount (TypeReference type)
2508 GenericInstanceType inst = type as GenericInstanceType;
2510 ? inst.GenericArguments.Count
2511 : type.GenericParameters.Count;
2514 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
2516 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
2517 List<TypeReference> userInterfaces = new List<TypeReference> ();
2518 foreach (TypeReference iface in type.Interfaces) {
2519 TypeReference lookup = iface.Resolve () ?? iface;
2520 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
2521 userInterfaces.Add (iface);
2523 return userInterfaces;
2526 private static string GetQualifiedTypeName (TypeReference type)
2528 return "[" + type.Scope.Name + "]" + type.FullName;
2531 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
2533 HashSet<string> inheritedInterfaces = new HashSet<string> ();
2534 Action<TypeDefinition> a = null;
2536 if (t == null) return;
2537 foreach (TypeReference r in t.Interfaces) {
2538 inheritedInterfaces.Add (GetQualifiedTypeName (r));
2542 TypeReference baseRef = type.BaseType;
2543 while (baseRef != null) {
2544 TypeDefinition baseDef = baseRef.Resolve ();
2545 if (baseDef != null) {
2547 baseRef = baseDef.BaseType;
2552 foreach (TypeReference r in type.Interfaces)
2554 return inheritedInterfaces;
2558 class DocsTypeInfo {
2559 public TypeDefinition Type;
2560 public XmlReader EcmaDocs;
2562 public DocsTypeInfo (TypeDefinition type, XmlReader docs)
2565 this.EcmaDocs = docs;
2569 class DocsNodeInfo {
2570 public DocsNodeInfo (XmlElement node)
2575 public DocsNodeInfo (XmlElement node, TypeDefinition type)
2581 public DocsNodeInfo (XmlElement node, IMemberReference member)
2584 SetMemberInfo (member);
2587 void SetType (TypeDefinition type)
2590 throw new ArgumentNullException ("type");
2591 GenericParameters = new List<GenericParameter> (type.GenericParameters.Cast<GenericParameter> ());
2592 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
2593 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
2594 for (int i = 0; i < declTypes.Count - 1; ++i) {
2595 int remove = System.Math.Min (maxGenArgs,
2596 DocUtils.GetGenericArgumentCount (declTypes [i]));
2597 maxGenArgs -= remove;
2598 while (remove-- > 0)
2599 GenericParameters.RemoveAt (0);
2601 if (DocUtils.IsDelegate (type)) {
2602 Parameters = type.GetMethod("Invoke").Parameters;
2603 ReturnType = type.GetMethod("Invoke").ReturnType.ReturnType;
2605 SetSlashDocs (type);
2608 void SetMemberInfo (IMemberReference member)
2611 throw new ArgumentNullException ("member");
2612 ReturnIsReturn = true;
2616 if (member is MethodReference ) {
2617 MethodReference mr = (MethodReference) member;
2618 Parameters = mr.Parameters;
2619 if (mr.IsGenericMethod ()) {
2620 GenericParameters = new List<GenericParameter> (mr.GenericParameters.Cast<GenericParameter> ());
2623 else if (member is PropertyDefinition) {
2624 Parameters = ((PropertyDefinition) member).Parameters;
2627 if (member is MethodDefinition) {
2628 ReturnType = ((MethodDefinition) member).ReturnType.ReturnType;
2629 } else if (member is PropertyDefinition) {
2630 ReturnType = ((PropertyDefinition) member).PropertyType;
2631 ReturnIsReturn = false;
2634 // no remarks section for enum members
2635 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
2637 SetSlashDocs (member);
2640 private void SetSlashDocs (IMemberReference member)
2642 if (MDocUpdater.slashdocs == null)
2645 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
2646 if (slashdocsig != null)
2647 SlashDocs = MDocUpdater.slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
2650 public TypeReference ReturnType;
2651 public List<GenericParameter> GenericParameters;
2652 public ParameterDefinitionCollection Parameters;
2653 public bool ReturnIsReturn;
2654 public XmlElement Node;
2655 public bool AddRemarks = true;
2656 public XmlNode SlashDocs;
2657 public XmlReader EcmaDocs;
2658 public IMemberReference Member;
2661 class DocumentationEnumerator {
2663 public virtual IEnumerable<DocsTypeInfo> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2665 return GetDocumentationTypes (assembly, forTypes, null);
2668 protected IEnumerable<DocsTypeInfo> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2670 foreach (TypeDefinition type in assembly.GetTypes()) {
2671 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
2673 if (seen != null && seen.Contains (type.FullName))
2675 yield return new DocsTypeInfo (type, null);
2676 foreach (TypeDefinition nested in type.NestedTypes)
2677 yield return new DocsTypeInfo (nested, null);
2681 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2683 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
2684 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
2685 oldmember.RemoveAttribute ("__monodocer-seen__");
2688 IMemberReference m = GetMember (type, new DocumentationMember (oldmember));
2690 yield return new DocsNodeInfo (oldmember);
2693 yield return new DocsNodeInfo (oldmember, m);
2698 protected static IMemberReference GetMember (TypeDefinition type, DocumentationMember member)
2700 string membertype = member.MemberType;
2702 string returntype = member.ReturnType;
2704 string docName = member.MemberName;
2705 string[] docTypeParams = GetTypeParameters (docName);
2707 // Loop through all members in this type with the same name
2708 foreach (IMemberReference mi in GetReflectionMembers (type, docName)) {
2709 if (mi is TypeDefinition) continue;
2710 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
2712 string sig = MDocUpdater.MakeMemberSignature(mi);
2713 if (sig == null) continue; // not publicly visible
2715 ParameterDefinitionCollection pis = null;
2716 string[] typeParams = null;
2717 if (mi is MethodDefinition) {
2718 MethodDefinition mb = (MethodDefinition) mi;
2719 pis = mb.Parameters;
2720 if (docTypeParams != null && mb.IsGenericMethod ()) {
2721 GenericParameterCollection args = mb.GenericParameters;
2722 if (args.Count == docTypeParams.Length) {
2723 typeParams = args.Cast<GenericParameter> ().Select (p => p.Name).ToArray ();
2727 else if (mi is PropertyDefinition)
2728 pis = ((PropertyDefinition)mi).Parameters;
2730 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
2731 int pcount = pis == null ? 0 : pis.Count;
2732 if (mcount != pcount)
2735 MethodDefinition mDef = mi as MethodDefinition;
2736 if (mDef != null && !mDef.IsConstructor) {
2737 // Casting operators can overload based on return type.
2738 if (returntype != GetReplacedString (
2739 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType.ReturnType),
2740 typeParams, docTypeParams)) {
2748 for (int i = 0; i < pis.Count; i++) {
2749 string paramType = GetReplacedString (
2750 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
2751 typeParams, docTypeParams);
2752 if (paramType != (string) member.Parameters [i]) {
2757 if (!good) continue;
2765 static string[] GetTypeParameters (string docName)
2767 if (docName [docName.Length-1] != '>')
2769 StringList types = new StringList ();
2770 int endToken = docName.Length-2;
2771 int i = docName.Length-2;
2773 if (docName [i] == ',' || docName [i] == '<') {
2774 types.Add (docName.Substring (i + 1, endToken - i));
2777 if (docName [i] == '<')
2782 return types.ToArray ();
2785 protected static IEnumerable<IMemberReference> GetReflectionMembers (TypeDefinition type, string docName)
2787 // need to worry about 4 forms of //@MemberName values:
2788 // 1. "Normal" (non-generic) member names: GetEnumerator
2790 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
2791 // - try as-is, and try type.member (due to "kludge" for property
2793 // 3. "Normal" Generic member names: Sort<T> (CSC)
2794 // - need to remove generic parameters --> "Sort"
2795 // 4. Explicitly-implemented interface members for generic interfaces:
2796 // -- System.Collections.Generic.IEnumerable<T>.Current
2797 // - Try as-is, and try type.member, *keeping* the generic parameters.
2798 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
2799 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
2800 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
2801 // this as (1) or (2).
2802 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
2804 foreach (IMemberReference mi in type.GetMembers (docName))
2806 if (CountChars (docName, '.') > 0)
2807 // might be a property; try only type.member instead of
2808 // namespace.type.member.
2809 foreach (IMemberReference mi in
2810 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
2817 int startLt, startType, startMethod;
2818 startLt = startType = startMethod = -1;
2819 for (int i = 0; i < docName.Length; ++i) {
2820 switch (docName [i]) {
2829 if (numLt == 0 && (i + 1) < docName.Length)
2830 // there's another character in docName, so this <...> sequence is
2831 // probably part of a generic type -- case 4.
2835 startType = startMethod;
2841 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
2843 foreach (IMemberReference mi in type.GetMembers (refName))
2847 foreach (IMemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
2850 // If we _still_ haven't found it, we've hit another generic naming issue:
2851 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
2852 // explicitly-implemented METHOD names (not properties), e.g.
2853 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
2854 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
2855 // which the XML docs will contain.
2857 // Alas, we can't derive the Mono name from docName, so we need to iterate
2858 // over all member names, convert them into CSC format, and compare... :-(
2861 foreach (IMemberReference mi in type.GetMembers ()) {
2862 if (MDocUpdater.GetMemberName (mi) == docName)
2867 static string GetReplacedString (string typeName, string[] from, string[] to)
2871 for (int i = 0; i < from.Length; ++i)
2872 typeName = typeName.Replace (from [i], to [i]);
2876 private static int CountChars (string s, char c)
2879 for (int i = 0; i < s.Length; ++i) {
2887 class EcmaDocumentationEnumerator : DocumentationEnumerator {
2892 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
2895 this.ecmadocs = ecmaDocs;
2898 public override IEnumerable<DocsTypeInfo> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2900 HashSet<string> seen = new HashSet<string> ();
2901 return GetDocumentationTypes (assembly, forTypes, seen)
2902 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
2905 IEnumerable<DocsTypeInfo> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2908 while (ecmadocs.Read ()) {
2909 switch (ecmadocs.Name) {
2911 if (typeDepth == -1)
2912 typeDepth = ecmadocs.Depth;
2913 if (ecmadocs.NodeType != XmlNodeType.Element)
2915 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
2917 string typename = ecmadocs.GetAttribute ("FullName");
2918 string typename2 = MDocUpdater.GetTypeFileName (typename);
2919 if (forTypes != null &&
2920 forTypes.BinarySearch (typename) < 0 &&
2921 typename != typename2 &&
2922 forTypes.BinarySearch (typename2) < 0)
2925 if ((t = assembly.GetType (typename)) == null &&
2926 (t = assembly.GetType (typename2)) == null)
2928 seen.Add (typename);
2929 if (typename != typename2)
2930 seen.Add (typename2);
2931 Console.WriteLine (" Import: {0}", t.FullName);
2932 yield return new DocsTypeInfo (t, ecmadocs);
2941 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2943 return GetMembers (basefile, type)
2944 .Concat (base.GetDocumentationMembers (basefile, type));
2947 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
2949 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
2952 if (ecmadocs.IsEmptyElement)
2955 int membersDepth = ecmadocs.Depth;
2957 while (go && ecmadocs.Read ()) {
2958 switch (ecmadocs.Name) {
2960 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
2962 DocumentationMember dm = new DocumentationMember (ecmadocs);
2963 string xp = MDocUpdater.GetXPathForMember (dm);
2964 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
2966 if (oldmember == null) {
2967 m = GetMember (type, dm);
2969 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2970 type.FullName, dm.MemberSignatures ["C#"]);
2971 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
2974 // oldmember lookup may have failed due to type parameter renames.
2976 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
2977 if (oldmember == null) {
2978 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
2979 oldmember = basefile.CreateElement ("Member");
2980 oldmember.SetAttribute ("MemberName", dm.MemberName);
2981 members.AppendChild (oldmember);
2982 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
2983 XmlElement ms = basefile.CreateElement ("MemberSignature");
2984 ms.SetAttribute ("Language", key);
2985 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
2986 oldmember.AppendChild (ms);
2988 oldmember.SetAttribute ("__monodocer-seen__", "true");
2989 Console.WriteLine ("Member Added: {0}", MDocUpdater.MakeMemberSignature (m));
2994 m = GetMember (type, new DocumentationMember (oldmember));
2996 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2997 type.FullName, dm.MemberSignatures ["C#"]);
3000 oldmember.SetAttribute ("__monodocer-seen__", "true");
3002 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
3003 if (ecmadocs.Name != "Docs")
3004 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
3005 node.EcmaDocs = ecmadocs;
3010 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
3019 abstract class DocumentationImporter {
3021 public abstract void FillType (XmlElement type);
3022 public abstract void FillMember (XmlElement member);
3025 class DocumentationMember {
3026 public StringToStringMap MemberSignatures = new StringToStringMap ();
3027 public string ReturnType;
3028 public StringList Parameters;
3029 public string MemberName;
3030 public string MemberType;
3032 public DocumentationMember (XmlReader reader)
3034 MemberName = reader.GetAttribute ("MemberName");
3035 int depth = reader.Depth;
3037 StringList p = new StringList ();
3039 if (reader.NodeType != XmlNodeType.Element)
3041 switch (reader.Name) {
3042 case "MemberSignature":
3043 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3046 MemberType = reader.ReadElementString ();
3049 if (reader.Depth == depth + 2)
3050 ReturnType = reader.ReadElementString ();
3053 if (reader.Depth == depth + 2)
3054 p.Add (reader.GetAttribute ("Type"));
3057 if (reader.Depth == depth + 1)
3061 } while (go && reader.Read () && reader.Depth >= depth);
3067 public DocumentationMember (XmlNode node)
3069 MemberName = node.Attributes ["MemberName"].Value;
3070 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3071 XmlAttribute l = n.Attributes ["Language"];
3072 XmlAttribute v = n.Attributes ["Value"];
3073 if (l != null && v != null)
3074 MemberSignatures [l.Value] = v.Value;
3076 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3077 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
3079 ReturnType = rt.InnerText;
3080 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
3082 Parameters = new StringList (p.Count);
3083 for (int i = 0; i < p.Count; ++i)
3084 Parameters.Add (p [i].Attributes ["Type"].Value);
3089 public enum MemberFormatterState {
3092 WithinGenericTypeContainer,
3095 public abstract class MemberFormatter {
3096 public virtual string GetName (IMemberReference member)
3098 TypeReference type = member as TypeReference;
3100 return GetTypeName (type);
3101 MethodReference method = member as MethodReference;
3102 if (method != null && method.Name == ".ctor") // method.IsConstructor
3103 return GetConstructorName (method);
3105 return GetMethodName (method);
3106 PropertyReference prop = member as PropertyReference;
3108 return GetPropertyName (prop);
3109 FieldReference field = member as FieldReference;
3111 return GetFieldName (field);
3112 EventReference e = member as EventReference;
3114 return GetEventName (e);
3115 throw new NotSupportedException ("Can't handle: " +
3116 (member == null ? "null" : member.GetType().ToString()));
3119 protected virtual string GetTypeName (TypeReference type)
3122 throw new ArgumentNullException ("type");
3123 return _AppendTypeName (new StringBuilder (type.Name.Length), type).ToString ();
3126 protected virtual char[] ArrayDelimeters {
3127 get {return new char[]{'[', ']'};}
3130 protected virtual MemberFormatterState MemberFormatterState { get; set; }
3132 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type)
3134 if (type is ArrayType) {
3135 TypeSpecification spec = type as TypeSpecification;
3136 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3137 .Append (ArrayDelimeters [0]);
3138 var origState = MemberFormatterState;
3139 MemberFormatterState = MemberFormatterState.WithinArray;
3140 ArrayType array = (ArrayType) type;
3141 int rank = array.Rank;
3143 buf.Append (new string (',', rank-1));
3144 MemberFormatterState = origState;
3145 return buf.Append (ArrayDelimeters [1]);
3147 if (type is ReferenceType) {
3148 return AppendRefTypeName (buf, type);
3150 if (type is PointerType) {
3151 return AppendPointerTypeName (buf, type);
3153 AppendNamespace (buf, type);
3154 if (type is GenericParameter) {
3155 return AppendTypeName (buf, type);
3157 GenericInstanceType genInst = type as GenericInstanceType;
3158 if (type.GenericParameters.Count == 0 &&
3159 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3160 return AppendFullTypeName (buf, type);
3162 return AppendGenericType (buf, type);
3165 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3167 string ns = DocUtils.GetNamespace (type);
3168 if (ns != null && ns.Length > 0)
3169 buf.Append (ns).Append ('.');
3173 private StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type)
3175 if (type.DeclaringType != null)
3176 AppendFullTypeName (buf, type.DeclaringType).Append (NestedTypeSeparator);
3177 return AppendTypeName (buf, type);
3180 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3182 return AppendTypeName (buf, type.Name);
3185 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3187 int n = typename.IndexOf ("`");
3189 return buf.Append (typename.Substring (0, n));
3190 return buf.Append (typename);
3193 protected virtual string RefTypeModifier {
3197 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type)
3199 TypeSpecification spec = type as TypeSpecification;
3200 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3201 .Append (RefTypeModifier);
3204 protected virtual string PointerModifier {
3208 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type)
3210 TypeSpecification spec = type as TypeSpecification;
3211 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3212 .Append (PointerModifier);
3215 protected virtual char[] GenericTypeContainer {
3216 get {return new char[]{'<', '>'};}
3219 protected virtual char NestedTypeSeparator {
3223 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3225 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3226 type is GenericInstanceType ? type.GetOriginalType () : type);
3227 List<TypeReference> genArgs = GetGenericArguments (type);
3230 bool insertNested = false;
3231 foreach (var decl in decls) {
3232 TypeReference declDef = decl.Resolve () ?? decl;
3234 buf.Append (NestedTypeSeparator);
3236 insertNested = true;
3237 AppendTypeName (buf, declDef);
3238 int ac = DocUtils.GetGenericArgumentCount (declDef);
3242 buf.Append (GenericTypeContainer [0]);
3243 var origState = MemberFormatterState;
3244 MemberFormatterState = MemberFormatterState.WithinGenericTypeContainer;
3245 _AppendTypeName (buf, genArgs [argIdx++]);
3246 for (int i = 1; i < c; ++i)
3247 _AppendTypeName (buf.Append (","), genArgs [argIdx++]);
3248 MemberFormatterState = origState;
3249 buf.Append (GenericTypeContainer [1]);
3255 private List<TypeReference> GetGenericArguments (TypeReference type)
3257 var args = new List<TypeReference> ();
3258 GenericInstanceType inst = type as GenericInstanceType;
3260 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
3262 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
3266 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3271 protected virtual string GetConstructorName (MethodReference constructor)
3273 return constructor.Name;
3276 protected virtual string GetMethodName (MethodReference method)
3281 protected virtual string GetPropertyName (PropertyReference property)
3283 return property.Name;
3286 protected virtual string GetFieldName (FieldReference field)
3291 protected virtual string GetEventName (EventReference e)
3296 public virtual string GetDeclaration (IMemberReference member)
3299 throw new ArgumentNullException ("member");
3300 TypeDefinition type = member as TypeDefinition;
3302 return GetTypeDeclaration (type);
3303 MethodDefinition method = member as MethodDefinition;
3304 if (method != null && method.IsConstructor)
3305 return GetConstructorDeclaration (method);
3307 return GetMethodDeclaration (method);
3308 PropertyDefinition prop = member as PropertyDefinition;
3310 return GetPropertyDeclaration (prop);
3311 FieldDefinition field = member as FieldDefinition;
3313 return GetFieldDeclaration (field);
3314 EventDefinition e = member as EventDefinition;
3316 return GetEventDeclaration (e);
3317 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3320 protected virtual string GetTypeDeclaration (TypeDefinition type)
3323 throw new ArgumentNullException ("type");
3324 StringBuilder buf = new StringBuilder (type.Name.Length);
3325 _AppendTypeName (buf, type);
3326 AppendGenericTypeConstraints (buf, type);
3327 return buf.ToString ();
3330 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
3332 return GetConstructorName (constructor);
3335 protected virtual string GetMethodDeclaration (MethodDefinition method)
3337 // Special signature for destructors.
3338 if (method.Name == "Finalize" && method.Parameters.Count == 0)
3339 return GetFinalizerName (method);
3341 StringBuilder buf = new StringBuilder ();
3343 AppendVisibility (buf, method);
3344 if (buf.Length == 0 &&
3345 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3348 AppendModifiers (buf, method);
3350 if (buf.Length != 0)
3352 buf.Append (GetName (method.ReturnType.ReturnType)).Append (" ");
3354 AppendMethodName (buf, method);
3355 AppendGenericMethod (buf, method).Append (" ");
3356 AppendParameters (buf, method, method.Parameters);
3357 AppendGenericMethodConstraints (buf, method);
3358 return buf.ToString ();
3361 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3363 return buf.Append (method.Name);
3366 protected virtual string GetFinalizerName (MethodDefinition method)
3371 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3376 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3381 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3386 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3391 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3396 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
3398 return GetPropertyName (property);
3401 protected virtual string GetFieldDeclaration (FieldDefinition field)
3403 return GetFieldName (field);
3406 protected virtual string GetEventDeclaration (EventDefinition e)
3408 return GetEventName (e);
3412 class CSharpFullMemberFormatter : MemberFormatter {
3414 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3416 string ns = DocUtils.GetNamespace (type);
3417 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
3418 buf.Append (ns).Append ('.');
3422 private string GetCSharpType (string t)
3425 case "System.Byte": return "byte";
3426 case "System.SByte": return "sbyte";
3427 case "System.Int16": return "short";
3428 case "System.Int32": return "int";
3429 case "System.Int64": return "long";
3431 case "System.UInt16": return "ushort";
3432 case "System.UInt32": return "uint";
3433 case "System.UInt64": return "ulong";
3435 case "System.Single": return "float";
3436 case "System.Double": return "double";
3437 case "System.Decimal": return "decimal";
3438 case "System.Boolean": return "bool";
3439 case "System.Char": return "char";
3440 case "System.Void": return "void";
3441 case "System.String": return "string";
3442 case "System.Object": return "object";
3447 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3449 if (type is GenericParameter)
3450 return AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
3451 string t = type.FullName;
3452 if (!t.StartsWith ("System.")) {
3453 return base.AppendTypeName (buf, type);
3456 string s = GetCSharpType (t);
3458 return buf.Append (s);
3460 return base.AppendTypeName (buf, type);
3463 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
3465 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeContainer)
3467 GenericParameterAttributes attrs = type.Attributes;
3468 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
3469 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
3473 buf.Append ("out ");
3477 protected override string GetTypeDeclaration (TypeDefinition type)
3479 string visibility = GetTypeVisibility (type.Attributes);
3480 if (visibility == null)
3483 StringBuilder buf = new StringBuilder ();
3485 buf.Append (visibility);
3488 MemberFormatter full = new CSharpFullMemberFormatter ();
3490 if (DocUtils.IsDelegate (type)) {
3491 buf.Append("delegate ");
3492 MethodDefinition invoke = type.GetMethod ("Invoke");
3493 buf.Append (full.GetName (invoke.ReturnType.ReturnType)).Append (" ");
3494 buf.Append (GetName (type));
3495 AppendParameters (buf, invoke, invoke.Parameters);
3496 AppendGenericTypeConstraints (buf, type);
3499 return buf.ToString();
3502 if (type.IsAbstract && !type.IsInterface)
3503 buf.Append("abstract ");
3504 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
3505 buf.Append("sealed ");
3506 buf.Replace ("abstract sealed", "static");
3508 buf.Append (GetTypeKind (type));
3510 buf.Append (GetCSharpType (type.FullName) == null
3515 TypeReference basetype = type.BaseType;
3516 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
3519 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
3520 .Select (iface => full.GetName (iface))
3524 if (basetype != null || interface_names.Count > 0)
3527 if (basetype != null) {
3528 buf.Append (full.GetName (basetype));
3529 if (interface_names.Count > 0)
3533 for (int i = 0; i < interface_names.Count; i++){
3536 buf.Append (interface_names [i]);
3538 AppendGenericTypeConstraints (buf, type);
3541 return buf.ToString ();
3544 static string GetTypeKind (TypeDefinition t)
3550 if (t.IsClass || t.FullName == "System.Enum")
3554 throw new ArgumentException(t.FullName);
3557 static string GetTypeVisibility (TypeAttributes ta)
3559 switch (ta & TypeAttributes.VisibilityMask) {
3560 case TypeAttributes.Public:
3561 case TypeAttributes.NestedPublic:
3564 case TypeAttributes.NestedFamily:
3565 case TypeAttributes.NestedFamORAssem:
3573 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3575 if (type.GenericParameters.Count == 0)
3577 return AppendConstraints (buf, type.GenericParameters);
3580 private StringBuilder AppendConstraints (StringBuilder buf, GenericParameterCollection genArgs)
3582 foreach (GenericParameter genArg in genArgs) {
3583 GenericParameterAttributes attrs = genArg.Attributes;
3584 ConstraintCollection constraints = genArg.Constraints;
3585 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
3588 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
3589 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
3590 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
3593 if (!isref && !isvt && !isnew && constraints.Count == 0)
3595 buf.Append (" where ").Append (genArg.Name).Append (" : ");
3597 buf.Append ("class");
3601 buf.Append ("struct");
3604 if (constraints.Count > 0 && !isvt) {
3607 buf.Append (GetTypeName (constraints [0]));
3608 for (int i = 1; i < constraints.Count; ++i)
3609 buf.Append (", ").Append (GetTypeName (constraints [i]));
3611 if (isnew && !isvt) {
3614 buf.Append ("new()");
3620 protected override string GetConstructorDeclaration (MethodDefinition constructor)
3622 StringBuilder buf = new StringBuilder ();
3623 AppendVisibility (buf, constructor);
3624 if (buf.Length == 0)
3628 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
3629 AppendParameters (buf, constructor, constructor.Parameters);
3632 return buf.ToString ();
3635 protected override string GetMethodDeclaration (MethodDefinition method)
3637 string decl = base.GetMethodDeclaration (method);
3643 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3645 if (DocUtils.IsExplicitlyImplemented (method)) {
3646 TypeReference iface;
3647 MethodReference ifaceMethod;
3648 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3649 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3651 .Append (ifaceMethod.Name);
3653 return base.AppendMethodName (buf, method);
3656 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3658 if (method.GenericParameters.Count == 0)
3660 return AppendConstraints (buf, method.GenericParameters);
3663 protected override string RefTypeModifier {
3667 protected override string GetFinalizerName (MethodDefinition method)
3669 return "~" + method.DeclaringType.Name + " ()";
3672 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3676 if (method.IsPublic)
3677 return buf.Append ("public");
3678 if (method.IsFamily || method.IsFamilyOrAssembly)
3679 return buf.Append ("protected");
3683 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3685 string modifiers = String.Empty;
3686 if (method.IsStatic) modifiers += " static";
3687 if (method.IsVirtual && !method.IsAbstract) {
3688 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3689 else modifiers += " override";
3691 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
3692 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
3693 if (method.IsFinal) modifiers += " sealed";
3694 if (modifiers == " virtual sealed") modifiers = "";
3696 return buf.Append (modifiers);
3699 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3701 if (method.IsGenericMethod ()) {
3702 GenericParameterCollection args = method.GenericParameters;
3703 if (args.Count > 0) {
3705 buf.Append (args [0].Name);
3706 for (int i = 1; i < args.Count; ++i)
3707 buf.Append (",").Append (args [i].Name);
3714 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3716 return AppendParameters (buf, method, parameters, '(', ')');
3719 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters, char begin, char end)
3723 if (parameters.Count > 0) {
3724 if (DocUtils.IsExtensionMethod (method))
3725 buf.Append ("this ");
3726 AppendParameter (buf, parameters [0]);
3727 for (int i = 1; i < parameters.Count; ++i) {
3729 AppendParameter (buf, parameters [i]);
3733 return buf.Append (end);
3736 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
3738 if (parameter.ParameterType is ReferenceType) {
3739 if (parameter.IsOut)
3740 buf.Append ("out ");
3742 buf.Append ("ref ");
3744 buf.Append (GetName (parameter.ParameterType)).Append (" ");
3745 return buf.Append (parameter.Name);
3748 protected override string GetPropertyDeclaration (PropertyDefinition property)
3750 MethodDefinition method;
3752 string get_visible = null;
3753 if ((method = property.GetMethod) != null &&
3754 (DocUtils.IsExplicitlyImplemented (method) ||
3755 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3756 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3757 string set_visible = null;
3758 if ((method = property.SetMethod) != null &&
3759 (DocUtils.IsExplicitlyImplemented (method) ||
3760 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3761 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3763 if ((set_visible == null) && (get_visible == null))
3767 StringBuilder buf = new StringBuilder ();
3768 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
3769 buf.Append (visibility = get_visible);
3770 else if (set_visible != null && get_visible == null)
3771 buf.Append (visibility = set_visible);
3773 buf.Append (visibility = "public");
3775 // Pick an accessor to use for static/virtual/override/etc. checks.
3776 method = property.SetMethod;
3778 method = property.GetMethod;
3780 string modifiers = String.Empty;
3781 if (method.IsStatic) modifiers += " static";
3782 if (method.IsVirtual && !method.IsAbstract) {
3783 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
3784 modifiers += " virtual";
3786 modifiers += " override";
3788 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
3789 if (method.IsAbstract && !declDef.IsInterface)
3790 modifiers += " abstract";
3792 modifiers += " sealed";
3793 if (modifiers == " virtual sealed")
3795 buf.Append (modifiers).Append (' ');
3797 buf.Append (GetName (property.PropertyType)).Append (' ');
3799 IEnumerable<IMemberReference> defs = property.DeclaringType.GetDefaultMembers ();
3800 string name = property.Name;
3801 foreach (IMemberReference mi in defs) {
3802 if (mi == property) {
3807 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
3809 if (property.Parameters.Count != 0) {
3810 AppendParameters (buf, method, property.Parameters, '[', ']');
3814 if (set_visible != null) {
3815 if (set_visible != visibility)
3816 buf.Append (' ').Append (set_visible);
3817 buf.Append (" set;");
3819 if (get_visible != null) {
3820 if (get_visible != visibility)
3821 buf.Append (' ').Append (get_visible);
3822 buf.Append (" get;");
3826 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
3829 protected override string GetFieldDeclaration (FieldDefinition field)
3831 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
3832 if (declType.IsEnum && field.Name == "value__")
3833 return null; // This member of enums aren't documented.
3835 StringBuilder buf = new StringBuilder ();
3836 AppendFieldVisibility (buf, field);
3837 if (buf.Length == 0)
3840 if (declType.IsEnum)
3843 if (field.IsStatic && !field.IsLiteral)
3844 buf.Append (" static");
3845 if (field.IsInitOnly)
3846 buf.Append (" readonly");
3847 if (field.IsLiteral)
3848 buf.Append (" const");
3850 buf.Append (' ').Append (GetName (field.FieldType)).Append (' ');
3851 buf.Append (field.Name);
3852 AppendFieldValue (buf, field);
3855 return buf.ToString ();
3858 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
3861 return buf.Append ("public");
3862 if (field.IsFamily || field.IsFamilyOrAssembly)
3863 return buf.Append ("protected");
3867 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
3869 // enums have a value__ field, which we ignore
3870 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
3871 field.DeclaringType.IsGenericType ())
3873 if (field.HasConstant && field.IsLiteral) {
3876 val = field.Constant;
3881 buf.Append (" = ").Append ("null");
3882 else if (val is Enum)
3883 buf.Append (" = ").Append (val.ToString ());
3884 else if (val is IFormattable) {
3885 string value = ((IFormattable)val).ToString();
3887 value = "\"" + value + "\"";
3888 buf.Append (" = ").Append (value);
3894 protected override string GetEventDeclaration (EventDefinition e)
3896 StringBuilder buf = new StringBuilder ();
3897 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
3901 AppendModifiers (buf, e.AddMethod);
3903 buf.Append (" event ");
3904 buf.Append (GetName (e.EventType)).Append (' ');
3905 buf.Append (e.Name).Append (';');
3907 return buf.ToString ();
3911 class CSharpMemberFormatter : CSharpFullMemberFormatter {
3912 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3918 class DocTypeFullMemberFormatter : MemberFormatter {
3919 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
3921 protected override char NestedTypeSeparator {
3926 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
3927 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3933 class SlashDocMemberFormatter : MemberFormatter {
3935 protected override char[] GenericTypeContainer {
3936 get {return new char[]{'{', '}'};}
3939 private bool AddTypeCount = true;
3941 private TypeReference genDeclType;
3942 private MethodReference genDeclMethod;
3944 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3946 if (type is GenericParameter) {
3948 if (genDeclType != null) {
3949 GenericParameterCollection genArgs = genDeclType.GenericParameters;
3950 for (int i = 0; i < genArgs.Count; ++i) {
3951 if (genArgs [i].Name == type.Name) {
3952 buf.Append ('`').Append (i);
3957 if (genDeclMethod != null) {
3958 GenericParameterCollection genArgs = null;
3959 if (genDeclMethod.IsGenericMethod ()) {
3960 genArgs = genDeclMethod.GenericParameters;
3961 for (int i = 0; i < genArgs.Count; ++i) {
3962 if (genArgs [i].Name == type.Name) {
3963 buf.Append ("``").Append (i);
3969 if (genDeclType == null && genDeclMethod == null) {
3970 // Probably from within an explicitly implemented interface member,
3971 // where CSC uses parameter names instead of indices (why?), e.g.
3972 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
3973 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
3974 buf.Append (type.Name);
3976 if (buf.Length == l) {
3977 throw new Exception (string.Format (
3978 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
3979 type.Name, genDeclType, genDeclMethod));
3983 base.AppendTypeName (buf, type);
3985 int numArgs = type.GenericParameters.Count;
3986 if (type.DeclaringType != null)
3987 numArgs -= type.GenericParameters.Count;
3989 buf.Append ('`').Append (numArgs);
3996 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3999 base.AppendGenericType (buf, type);
4001 AppendType (buf, type);
4005 private StringBuilder AppendType (StringBuilder buf, TypeReference type)
4007 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
4008 bool insertNested = false;
4009 int prevParamCount = 0;
4010 foreach (var decl in decls) {
4012 buf.Append (NestedTypeSeparator);
4013 insertNested = true;
4014 base.AppendTypeName (buf, decl);
4015 int argCount = DocUtils.GetGenericArgumentCount (decl);
4016 int numArgs = argCount - prevParamCount;
4017 prevParamCount = argCount;
4019 buf.Append ('`').Append (numArgs);
4024 public override string GetDeclaration (IMemberReference member)
4026 TypeReference r = member as TypeReference;
4028 return "T:" + GetTypeName (r);
4030 return base.GetDeclaration (member);
4033 protected override string GetConstructorName (MethodReference constructor)
4035 return GetMethodDefinitionName (constructor, "#ctor");
4038 protected override string GetMethodName (MethodReference method)
4041 MethodDefinition methodDef = method as MethodDefinition;
4042 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
4045 TypeReference iface;
4046 MethodReference ifaceMethod;
4047 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
4048 AddTypeCount = false;
4049 name = GetTypeName (iface) + "." + ifaceMethod.Name;
4050 AddTypeCount = true;
4052 return GetMethodDefinitionName (method, name);
4055 private string GetMethodDefinitionName (MethodReference method, string name)
4057 StringBuilder buf = new StringBuilder ();
4058 buf.Append (GetTypeName (method.DeclaringType));
4060 buf.Append (name.Replace (".", "#"));
4061 if (method.IsGenericMethod ()) {
4062 GenericParameterCollection genArgs = method.GenericParameters;
4063 if (genArgs.Count > 0)
4064 buf.Append ("``").Append (genArgs.Count);
4066 ParameterDefinitionCollection parameters = method.Parameters;
4068 genDeclType = method.DeclaringType;
4069 genDeclMethod = method;
4070 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
4074 genDeclMethod = null;
4076 return buf.ToString ();
4079 private StringBuilder AppendParameters (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinitionCollection parameters)
4081 if (parameters.Count == 0)
4086 AppendParameter (buf, genArgs, parameters [0]);
4087 for (int i = 1; i < parameters.Count; ++i) {
4089 AppendParameter (buf, genArgs, parameters [i]);
4092 return buf.Append (')');
4095 private StringBuilder AppendParameter (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinition parameter)
4097 AddTypeCount = false;
4098 buf.Append (GetTypeName (parameter.ParameterType));
4099 AddTypeCount = true;
4103 protected override string GetPropertyName (PropertyReference property)
4107 PropertyDefinition propertyDef = property as PropertyDefinition;
4108 MethodDefinition method = null;
4109 if (propertyDef != null)
4110 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
4111 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
4112 name = property.Name;
4114 TypeReference iface;
4115 MethodReference ifaceMethod;
4116 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4117 AddTypeCount = false;
4118 name = string.Join ("#", new string[]{
4119 GetTypeName (iface).Replace (".", "#"),
4120 DocUtils.GetMember (property.Name)
4122 AddTypeCount = true;
4125 StringBuilder buf = new StringBuilder ();
4126 buf.Append (GetName (property.DeclaringType));
4129 ParameterDefinitionCollection parameters = property.Parameters;
4130 if (parameters.Count > 0) {
4131 genDeclType = property.DeclaringType;
4133 GenericParameterCollection genArgs = property.DeclaringType.GenericParameters;
4134 AppendParameter (buf, genArgs, parameters [0]);
4135 for (int i = 1; i < parameters.Count; ++i) {
4137 AppendParameter (buf, genArgs, parameters [i]);
4142 return buf.ToString ();
4145 protected override string GetFieldName (FieldReference field)
4147 return string.Format ("{0}.{1}",
4148 GetName (field.DeclaringType), field.Name);
4151 protected override string GetEventName (EventReference e)
4153 return string.Format ("{0}.{1}",
4154 GetName (e.DeclaringType), e.Name);
4157 protected override string GetTypeDeclaration (TypeDefinition type)
4159 string name = GetName (type);
4165 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4167 string name = GetName (constructor);
4173 protected override string GetMethodDeclaration (MethodDefinition method)
4175 string name = GetName (method);
4178 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4179 genDeclType = method.DeclaringType;
4180 genDeclMethod = method;
4181 name += "~" + GetName (method.ReturnType.ReturnType);
4183 genDeclMethod = null;
4188 protected override string GetPropertyDeclaration (PropertyDefinition property)
4190 string name = GetName (property);
4196 protected override string GetFieldDeclaration (FieldDefinition field)
4198 string name = GetName (field);
4204 protected override string GetEventDeclaration (EventDefinition e)
4206 string name = GetName (e);
4213 class FileNameMemberFormatter : SlashDocMemberFormatter {
4214 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4219 protected override char NestedTypeSeparator {