1 // Updater program for syncing Mono's ECMA-style documentation files
3 // By Joshua Tauberer <tauberer@for.net>
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Diagnostics;
9 using System.Globalization;
14 using System.Xml.XPath;
19 using MyXmlNodeList = System.Collections.Generic.List<System.Xml.XmlNode>;
20 using StringList = System.Collections.Generic.List<string>;
21 using StringToStringMap = System.Collections.Generic.Dictionary<string, string>;
22 using StringToXmlNodeMap = System.Collections.Generic.Dictionary<string, System.Xml.XmlNode>;
24 namespace Mono.Documentation {
26 class MDocUpdater : MDocCommand
29 List<AssemblyDefinition> assemblies;
30 readonly DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver();
34 bool no_assembly_versions, ignore_missing_types;
35 ExceptionLocations? exceptions;
37 internal int additions = 0, deletions = 0;
39 List<DocumentationImporter> importers = new List<DocumentationImporter> ();
41 DocumentationEnumerator docEnum;
45 static readonly MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
46 static readonly MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
48 static MemberFormatter[] typeFormatters = new MemberFormatter[]{
49 new CSharpMemberFormatter (),
50 new ILMemberFormatter (),
53 static MemberFormatter[] memberFormatters = new MemberFormatter[]{
54 new CSharpFullMemberFormatter (),
55 new ILFullMemberFormatter (),
58 internal static readonly MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
60 MyXmlNodeList extensionMethods = new MyXmlNodeList ();
62 public override void Run (IEnumerable<string> args)
64 show_exceptions = DebugOutput;
65 var types = new List<string> ();
66 var p = new OptionSet () {
68 "Delete removed members from the XML files.",
69 v => delete = v != null },
71 "Document potential exceptions that members can generate. {SOURCES} " +
72 "is a comma-separated list of:\n" +
73 " asm Method calls in same assembly\n" +
74 " depasm Method calls in dependent assemblies\n" +
75 " all Record all possible exceptions\n" +
76 " added Modifier; only create <exception/>s\n" +
77 " for NEW types/members\n" +
78 "If nothing is specified, then only exceptions from the member will " +
80 v => exceptions = ParseExceptionLocations (v) },
82 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
85 case "ignore-missing-types":
86 ignore_missing_types = true;
88 case "no-assembly-versions":
89 no_assembly_versions = true;
92 throw new Exception ("Unsupported flag `" + v + "'.");
95 { "fignore-missing-types",
96 "Do not report an error if a --type=TYPE type\nwas not found.",
97 v => ignore_missing_types = v != null },
98 { "fno-assembly-versions",
99 "Do not generate //AssemblyVersion elements.",
100 v => no_assembly_versions = v != null },
102 "Import documentation from {FILE}.",
103 v => AddImporter (v) },
105 "Check for assembly references in {DIRECTORY}.",
106 v => assemblyResolver.AddSearchDirectory (v) },
108 "Ignored for compatibility with update-ecma-xml.",
111 "Root {DIRECTORY} to generate/update documentation.",
114 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
115 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
116 v => assemblyResolver.AddSearchDirectory (Path.GetDirectoryName (v)) },
118 "Manually specify the assembly {VERSION} that new members were added in.",
121 "Only update documentation for {TYPE}.",
122 v => types.Add (v) },
124 var assemblies = Parse (p, args, "update",
125 "[OPTIONS]+ ASSEMBLIES",
126 "Create or update documentation from ASSEMBLIES.");
127 if (assemblies == null)
129 if (assemblies.Count == 0)
130 Error ("No assemblies specified.");
132 foreach (var dir in assemblies
133 .Where (a => a.Contains (Path.DirectorySeparatorChar))
134 .Select (a => Path.GetDirectoryName (a)))
135 assemblyResolver.AddSearchDirectory (dir);
137 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
140 throw new InvalidOperationException("The --out option is required.");
142 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
144 docEnum = docEnum ?? new DocumentationEnumerator ();
146 // PERFORM THE UPDATES
148 if (types.Count > 0) {
150 DoUpdateTypes (srcPath, types, srcPath);
153 else if (opts.@namespace != null)
154 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
155 Path.Combine (dest_dir, opts.@namespace));
158 DoUpdateAssemblies (srcPath, srcPath);
160 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
163 void AddImporter (string path)
166 XmlReader r = new XmlTextReader (path);
168 while (r.NodeType != XmlNodeType.Element) {
170 Error ("Unable to read XML file: {0}.", path);
172 if (r.LocalName == "doc") {
173 importers.Add (new MsxdocDocumentationImporter (path));
175 else if (r.LocalName == "Libraries") {
176 var ecmadocs = new XmlTextReader (path);
177 docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
178 importers.Add (new EcmaDocumentationImporter (ecmadocs));
181 Error ("Unsupported XML format within {0}.", path);
184 } catch (Exception e) {
185 Environment.ExitCode = 1;
186 Error ("Could not load XML file: {0}.", e.Message);
190 static ExceptionLocations ParseExceptionLocations (string s)
192 ExceptionLocations loc = ExceptionLocations.Member;
195 foreach (var type in s.Split (',')) {
197 case "added": loc |= ExceptionLocations.AddedMembers; break;
198 case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
199 case "asm": loc |= ExceptionLocations.Assembly; break;
200 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
201 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
207 internal void Warning (string format, params object[] args)
209 Message (TraceLevel.Warning, "mdoc: " + format, args);
212 private AssemblyDefinition LoadAssembly (string name)
214 AssemblyDefinition assembly = null;
216 assembly = AssemblyDefinition.ReadAssembly (name, new ReaderParameters { AssemblyResolver = assemblyResolver });
217 } catch (System.IO.FileNotFoundException) { }
219 if (assembly == null)
220 throw new InvalidOperationException("Assembly " + name + " not found.");
225 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
226 OrderTypeAttributes (element);
227 XmlTextWriter writer = new XmlTextWriter(output);
228 writer.Formatting = Formatting.Indented;
229 writer.Indentation = 2;
230 writer.IndentChar = ' ';
231 element.WriteTo(writer);
235 private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
237 Action<string> creator = file => {
238 using (var writer = OpenWrite (file, mode))
242 MdocFile.UpdateFile (filename, creator);
245 private static void OrderTypeAttributes (XmlElement e)
247 foreach (XmlElement type in e.SelectNodes ("//Type")) {
248 OrderTypeAttributes (type.Attributes);
252 static readonly string[] TypeAttributeOrder = {
253 "Name", "FullName", "FullNameSP", "Maintainer"
256 private static void OrderTypeAttributes (XmlAttributeCollection c)
258 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
259 for (int i = 0; i < c.Count; ++i) {
260 XmlAttribute a = c [i];
261 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
262 if (a.Name == TypeAttributeOrder [j]) {
268 for (int i = attrs.Length-1; i >= 0; --i) {
269 XmlAttribute n = attrs [i];
272 XmlAttribute r = null;
273 for (int j = i+1; j < attrs.Length; ++j) {
274 if (attrs [j] != null) {
282 c.InsertBefore (n, r);
286 private XmlDocument CreateIndexStub()
288 XmlDocument index = new XmlDocument();
290 XmlElement index_root = index.CreateElement("Overview");
291 index.AppendChild(index_root);
293 if (assemblies.Count == 0)
294 throw new Exception ("No assembly");
296 XmlElement index_assemblies = index.CreateElement("Assemblies");
297 index_root.AppendChild(index_assemblies);
299 XmlElement index_remarks = index.CreateElement("Remarks");
300 index_remarks.InnerText = "To be added.";
301 index_root.AppendChild(index_remarks);
303 XmlElement index_copyright = index.CreateElement("Copyright");
304 index_copyright.InnerText = "To be added.";
305 index_root.AppendChild(index_copyright);
307 XmlElement index_types = index.CreateElement("Types");
308 index_root.AppendChild(index_types);
313 private static void WriteNamespaceStub(string ns, string outdir) {
314 XmlDocument index = new XmlDocument();
316 XmlElement index_root = index.CreateElement("Namespace");
317 index.AppendChild(index_root);
319 index_root.SetAttribute("Name", ns);
321 XmlElement index_docs = index.CreateElement("Docs");
322 index_root.AppendChild(index_docs);
324 XmlElement index_summary = index.CreateElement("summary");
325 index_summary.InnerText = "To be added.";
326 index_docs.AppendChild(index_summary);
328 XmlElement index_remarks = index.CreateElement("remarks");
329 index_remarks.InnerText = "To be added.";
330 index_docs.AppendChild(index_remarks);
332 WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
333 writer => WriteXml (index.DocumentElement, writer));
336 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
338 var index = CreateIndexForTypes (dest);
340 var found = new HashSet<string> ();
341 foreach (AssemblyDefinition assembly in assemblies) {
342 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames)) {
343 string relpath = DoUpdateType (type, basepath, dest);
347 found.Add (type.FullName);
352 index.Add (assembly);
360 if (ignore_missing_types)
363 var notFound = from n in typenames where !found.Contains (n) select n;
365 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
368 class IndexForTypes {
374 XmlElement index_types;
375 XmlElement index_assemblies;
377 public IndexForTypes (MDocUpdater app, string indexFile, XmlDocument index)
380 this.indexFile = indexFile;
383 index_types = WriteElement (index.DocumentElement, "Types");
384 index_assemblies = WriteElement (index.DocumentElement, "Assemblies");
387 public void Add (AssemblyDefinition assembly)
389 if (index_assemblies.SelectSingleNode ("Assembly[@Name='" + assembly.Name.Name + "']") != null)
392 app.AddIndexAssembly (assembly, index_assemblies);
395 public void Add (TypeDefinition type)
397 app.AddIndexType (type, index_types);
402 SortIndexEntries (index_types);
403 WriteFile (indexFile, FileMode.Create,
404 writer => WriteXml (index.DocumentElement, writer));
408 IndexForTypes CreateIndexForTypes (string dest)
410 string indexFile = Path.Combine (dest, "index.xml");
411 if (File.Exists (indexFile))
413 return new IndexForTypes (this, indexFile, CreateIndexStub ());
416 public string DoUpdateType (TypeDefinition type, string basepath, string dest)
418 if (type.Namespace == null)
419 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
421 if (!IsPublic (type))
424 // Must get the A+B form of the type name.
425 string typename = GetTypeFileName(type);
427 string reltypefile = DocUtils.PathCombine (DocUtils.GetNamespace (type), typename + ".xml");
428 string typefile = Path.Combine (basepath, reltypefile);
429 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
431 string output = null;
434 } else if (dest == "-") {
437 output = Path.Combine (dest, reltypefile);
442 XmlDocument basefile = new XmlDocument();
444 basefile.Load(typefile);
445 } catch (Exception e) {
446 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
449 DoUpdateType2("Updating", basefile, type, output, false);
452 XmlElement td = StubType(type, output);
456 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
459 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
465 public void DoUpdateNS (string ns, string nspath, string outpath)
467 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
468 AssemblyDefinition assembly = assemblies [0];
470 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
471 XmlDocument basefile = new XmlDocument();
472 string typefile = Path.Combine(nspath, file.Name);
474 basefile.Load(typefile);
475 } catch (Exception e) {
476 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
480 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
481 TypeDefinition type = assembly.GetType(typename);
483 Warning ("Type no longer in assembly: " + typename);
487 seenTypes[type] = seenTypes;
488 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false);
491 // Stub types not in the directory
492 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
493 if (type.Namespace != ns || seenTypes.ContainsKey(type))
496 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"));
497 if (td == null) continue;
501 private static string GetTypeFileName (TypeReference type)
503 return filenameFormatter.GetName (type);
506 public static string GetTypeFileName (string typename)
508 StringBuilder filename = new StringBuilder (typename.Length);
512 for (int i = 0; i < typename.Length; ++i) {
513 char c = typename [i];
522 filename.Append ('`').Append ((numArgs+1).ToString());
537 return filename.ToString ();
540 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
542 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
543 index_assembly.SetAttribute ("Name", assembly.Name.Name);
544 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
546 AssemblyNameDefinition name = assembly.Name;
547 if (name.HasPublicKey) {
548 XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
549 var key = new StringBuilder (name.PublicKey.Length*3 + 2);
551 foreach (byte b in name.PublicKey)
552 key.AppendFormat ("{0,2:x2} ", b);
554 pubkey.InnerText = key.ToString ();
555 index_assembly.AppendChild (pubkey);
558 if (!string.IsNullOrEmpty (name.Culture)) {
559 XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
560 culture.InnerText = name.Culture;
561 index_assembly.AppendChild (culture);
564 MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
565 parent.AppendChild(index_assembly);
568 private void AddIndexType (TypeDefinition type, XmlElement index_types)
570 string typename = GetTypeFileName(type);
572 // Add namespace and type nodes into the index file as needed
573 string ns = DocUtils.GetNamespace (type);
574 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode ("Namespace[@Name='" + ns + "']");
575 if (nsnode == null) {
576 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
577 nsnode.SetAttribute ("Name", ns);
578 index_types.AppendChild (nsnode);
580 string doc_typename = GetDocTypeName (type);
581 XmlElement typenode = (XmlElement) nsnode.SelectSingleNode ("Type[@Name='" + typename + "']");
582 if (typenode == null) {
583 typenode = index_types.OwnerDocument.CreateElement ("Type");
584 typenode.SetAttribute ("Name", typename);
585 nsnode.AppendChild (typenode);
587 if (typename != doc_typename)
588 typenode.SetAttribute("DisplayName", doc_typename);
590 typenode.RemoveAttribute("DisplayName");
592 typenode.SetAttribute ("Kind", GetTypeKind (type));
595 private void DoUpdateAssemblies (string source, string dest)
597 string indexfile = dest + "/index.xml";
599 if (System.IO.File.Exists(indexfile)) {
600 index = new XmlDocument();
601 index.Load(indexfile);
604 ClearElement(index.DocumentElement, "Assembly");
605 ClearElement(index.DocumentElement, "Attributes");
607 index = CreateIndexStub();
610 string defaultTitle = "Untitled";
611 if (assemblies.Count == 1)
612 defaultTitle = assemblies[0].Name.Name;
613 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
615 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
616 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
617 index_assemblies.RemoveAll ();
620 HashSet<string> goodfiles = new HashSet<string> ();
622 foreach (AssemblyDefinition assm in assemblies) {
623 AddIndexAssembly (assm, index_assemblies);
624 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
627 SortIndexEntries (index_types);
629 CleanupFiles (dest, goodfiles);
630 CleanupIndexTypes (index_types, goodfiles);
631 CleanupExtensions (index_types);
633 WriteFile (indexfile, FileMode.Create,
634 writer => WriteXml(index.DocumentElement, writer));
637 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
639 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
641 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
642 string typename = GetTypeFileName(type);
643 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0)
646 string reltypepath = DoUpdateType (type, source, dest);
647 if (reltypepath == null)
650 // Add namespace and type nodes into the index file as needed
651 AddIndexType (type, index_types);
653 // Ensure the namespace index file exists
654 string onsdoc = DocUtils.PathCombine (dest, type.Namespace + ".xml");
655 string nsdoc = DocUtils.PathCombine (dest, "ns-" + type.Namespace + ".xml");
656 if (File.Exists (onsdoc)) {
657 File.Move (onsdoc, nsdoc);
660 if (!File.Exists (nsdoc)) {
661 Console.WriteLine("New Namespace File: " + type.Namespace);
662 WriteNamespaceStub(type.Namespace, dest);
665 goodfiles.Add (reltypepath);
669 private static void SortIndexEntries (XmlElement indexTypes)
671 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
672 XmlNodeComparer c = new AttributeNameComparer ();
673 SortXmlNodes (indexTypes, namespaces, c);
675 for (int i = 0; i < namespaces.Count; ++i)
676 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
679 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
681 MyXmlNodeList l = new MyXmlNodeList (children.Count);
682 for (int i = 0; i < children.Count; ++i)
683 l.Add (children [i]);
685 for (int i = l.Count - 1; i > 0; --i) {
686 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
690 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
692 public abstract int Compare (XmlNode x, XmlNode y);
694 public int Compare (object x, object y)
696 return Compare ((XmlNode) x, (XmlNode) y);
700 class AttributeNameComparer : XmlNodeComparer {
703 public AttributeNameComparer ()
708 public AttributeNameComparer (string attribute)
710 this.attribute = attribute;
713 public override int Compare (XmlNode x, XmlNode y)
715 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
719 class VersionComparer : XmlNodeComparer {
720 public override int Compare (XmlNode x, XmlNode y)
722 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
723 string a = GetVersion (x.InnerText);
724 string b = GetVersion (y.InnerText);
725 return new Version (a).CompareTo (new Version (b));
728 static string GetVersion (string v)
730 int n = v.IndexOf ("x");
733 return v.Substring (0, n-1);
737 private static string GetTypeKind (TypeDefinition type)
740 return "Enumeration";
741 if (type.IsValueType)
743 if (type.IsInterface)
745 if (DocUtils.IsDelegate (type))
747 if (type.IsClass || type.FullName == "System.Enum") // FIXME
749 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
752 private static bool IsPublic (TypeDefinition type)
754 TypeDefinition decl = type;
755 while (decl != null) {
756 if (!(decl.IsPublic || decl.IsNestedPublic)) {
759 decl = (TypeDefinition) decl.DeclaringType;
764 private void CleanupFiles (string dest, HashSet<string> goodfiles)
766 // Look for files that no longer correspond to types
767 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
768 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
769 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
770 if (!goodfiles.Contains (relTypeFile)) {
771 XmlDocument doc = new XmlDocument ();
772 doc.Load (typefile.FullName);
773 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
774 if (UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
775 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
776 WriteXml(doc.DocumentElement, writer);
777 goodfiles.Add (relTypeFile);
780 string newname = typefile.FullName + ".remove";
781 try { System.IO.File.Delete(newname); } catch (Exception) { }
782 try { typefile.MoveTo(newname); } catch (Exception) { }
783 Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
789 private static TextWriter OpenWrite (string path, FileMode mode)
791 var w = new StreamWriter (
792 new FileStream (path, mode),
793 new UTF8Encoding (false)
799 private string[] GetAssemblyVersions ()
801 return (from a in assemblies select GetAssemblyVersion (a)).ToArray ();
804 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
806 // Look for type nodes that no longer correspond to types
807 MyXmlNodeList remove = new MyXmlNodeList ();
808 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
809 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
810 if (!goodfiles.Contains (fulltypename)) {
811 remove.Add (typenode);
814 foreach (XmlNode n in remove)
815 n.ParentNode.RemoveChild (n);
818 private void CleanupExtensions (XmlElement index_types)
820 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
821 if (extensionMethods.Count == 0) {
824 index_types.SelectSingleNode ("/Overview").RemoveChild (e);
828 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
829 index_types.SelectSingleNode ("/Overview").AppendChild (e);
833 extensionMethods.Sort (DefaultExtensionMethodComparer);
834 foreach (XmlNode m in extensionMethods) {
835 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
839 class ExtensionMethodComparer : XmlNodeComparer {
840 public override int Compare (XmlNode x, XmlNode y)
842 XmlNode xLink = x.SelectSingleNode ("Member/Link");
843 XmlNode yLink = y.SelectSingleNode ("Member/Link");
845 int n = xLink.Attributes ["Type"].Value.CompareTo (
846 yLink.Attributes ["Type"].Value);
849 n = xLink.Attributes ["Member"].Value.CompareTo (
850 yLink.Attributes ["Member"].Value);
851 if (n == 0 && !object.ReferenceEquals (x, y))
852 throw new InvalidOperationException ("Duplicate extension method found!");
857 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
859 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince)
861 Console.WriteLine(message + ": " + type.FullName);
863 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
865 // Update type metadata
866 UpdateType(basefile.DocumentElement, type);
868 // Update existing members. Delete member nodes that no longer should be there,
869 // and remember what members are already documented so we don't add them again.
871 MyXmlNodeList todelete = new MyXmlNodeList ();
872 foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
873 XmlElement oldmember = info.Node;
874 MemberReference oldmember2 = info.Member;
875 string sig = oldmember2 != null ? memberFormatters [0].GetDeclaration (oldmember2) : null;
877 // Interface implementations and overrides are deleted from the docs
878 // unless the overrides option is given.
879 if (oldmember2 != null && sig == null)
882 // Deleted (or signature changed)
883 if (oldmember2 == null) {
884 if (UpdateAssemblyVersions (oldmember, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
886 DeleteMember ("Member Removed", output, oldmember, todelete);
891 if (seenmembers.ContainsKey (sig)) {
892 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
893 // ignore, already seen
895 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
896 DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
898 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
902 // Update signature information
905 seenmembers.Add (sig, oldmember);
907 foreach (XmlElement oldmember in todelete)
908 oldmember.ParentNode.RemoveChild (oldmember);
911 if (!DocUtils.IsDelegate (type)) {
912 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
913 foreach (MemberReference m in type.GetMembers()) {
914 if (m is TypeDefinition) continue;
916 string sig = memberFormatters [0].GetDeclaration (m);
917 if (sig == null) continue;
918 if (seenmembers.ContainsKey(sig)) continue;
920 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
921 if (mm == null) continue;
922 members.AppendChild( mm );
924 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
929 // Import code snippets from files
930 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
931 if (!(code is XmlElement)) continue;
932 string file = ((XmlElement)code).GetAttribute("src");
933 string lang = ((XmlElement)code).GetAttribute("lang");
935 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
937 code.InnerText = src;
941 if (insertSince && since != null) {
942 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
943 docs.AppendChild (CreateSinceNode (basefile));
947 XmlElement d = basefile.DocumentElement ["Docs"];
948 XmlElement m = basefile.DocumentElement ["Members"];
949 if (d != null && m != null)
950 basefile.DocumentElement.InsertBefore (
951 basefile.DocumentElement.RemoveChild (d), m);
956 WriteXml(basefile.DocumentElement, Console.Out);
958 FileInfo file = new FileInfo (output);
959 if (!file.Directory.Exists) {
960 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
961 file.Directory.Create ();
963 WriteFile (output, FileMode.Create,
964 writer => WriteXml(basefile.DocumentElement, writer));
968 private string GetCodeSource (string lang, string file)
971 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
972 // Grab the specified region
973 string region = "#region " + file.Substring (anchorStart + 4);
974 file = file.Substring (0, anchorStart + 3);
976 using (StreamReader reader = new StreamReader (file)) {
978 StringBuilder src = new StringBuilder ();
980 while ((line = reader.ReadLine ()) != null) {
981 if (line.Trim() == region) {
982 indent = line.IndexOf (region);
985 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
990 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
993 return src.ToString ();
995 } catch (Exception e) {
996 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
997 file, region, show_exceptions ? e.ToString () : e.Message);
1002 using (StreamReader reader = new StreamReader (file))
1003 return reader.ReadToEnd ();
1004 } catch (Exception e) {
1005 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
1010 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
1012 string format = output != null
1013 ? "{0}: File='{1}'; Signature='{4}'"
1014 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1018 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1019 member.Attributes ["MemberName"].Value,
1020 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
1021 if (!delete && MemberDocsHaveUserContent (member)) {
1022 Warning ("Member deletions must be enabled with the --delete option.");
1024 todelete.Add (member);
1029 class MemberComparer : XmlNodeComparer {
1030 public override int Compare (XmlNode x, XmlNode y)
1033 string xMemberName = x.Attributes ["MemberName"].Value;
1034 string yMemberName = y.Attributes ["MemberName"].Value;
1036 // generic methods *end* with '>'
1037 // it's possible for explicitly implemented generic interfaces to
1038 // contain <...> without being a generic method
1039 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1040 (r = xMemberName.CompareTo (yMemberName)) != 0)
1044 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1045 xMemberName = xMemberName.Substring (0, lt);
1046 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1047 yMemberName = yMemberName.Substring (0, lt);
1048 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1051 // if @MemberName matches, then it's either two different types of
1052 // members sharing the same name, e.g. field & property, or it's an
1053 // overloaded method.
1054 // for different type, sort based on MemberType value.
1055 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1056 y.SelectSingleNode ("MemberType").InnerText);
1060 // same type -- must be an overloaded method. Sort based on type
1061 // parameter count, then parameter count, then by the parameter
1063 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1064 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1065 if (xTypeParams.Count != yTypeParams.Count)
1066 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1067 for (int i = 0; i < xTypeParams.Count; ++i) {
1068 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1069 yTypeParams [i].Attributes ["Name"].Value);
1074 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1075 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1076 if (xParams.Count != yParams.Count)
1077 return xParams.Count <= yParams.Count ? -1 : 1;
1078 for (int i = 0; i < xParams.Count; ++i) {
1079 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1080 yParams [i].Attributes ["Type"].Value);
1084 // all parameters match, but return value might not match if it was
1085 // changed between one version and another.
1086 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1087 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1088 if (xReturn != null && yReturn != null) {
1089 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1098 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1100 private static void SortTypeMembers (XmlNode members)
1102 if (members == null)
1104 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1107 private static bool MemberDocsHaveUserContent (XmlNode e)
1109 e = (XmlElement)e.SelectSingleNode("Docs");
1110 if (e == null) return false;
1111 foreach (XmlElement d in e.SelectNodes("*"))
1112 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1117 // UPDATE HELPER FUNCTIONS
1119 // CREATE A STUB DOCUMENTATION FILE
1121 public XmlElement StubType (TypeDefinition type, string output)
1123 string typesig = typeFormatters [0].GetDeclaration (type);
1124 if (typesig == null) return null; // not publicly visible
1126 XmlDocument doc = new XmlDocument();
1127 XmlElement root = doc.CreateElement("Type");
1128 doc.AppendChild (root);
1130 DoUpdateType2 ("New Type", doc, type, output, true);
1135 private XmlElement CreateSinceNode (XmlDocument doc)
1137 XmlElement s = doc.CreateElement ("since");
1138 s.SetAttribute ("version", since);
1142 // STUBBING/UPDATING FUNCTIONS
1144 public void UpdateType (XmlElement root, TypeDefinition type)
1146 root.SetAttribute("Name", GetDocTypeName (type));
1147 root.SetAttribute("FullName", GetDocTypeFullName (type));
1149 foreach (MemberFormatter f in typeFormatters) {
1150 string element = "TypeSignature[@Language='" + f.Language + "']";
1151 WriteElementAttribute (root, element, "Language", f.Language);
1152 WriteElementAttribute (root, element, "Value", f.GetDeclaration (type));
1155 XmlElement ass = WriteElement(root, "AssemblyInfo");
1156 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1157 if (!no_assembly_versions) {
1158 UpdateAssemblyVersions (root, type, true);
1161 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1162 foreach (var version in versions)
1163 ass.RemoveChild (version);
1165 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1166 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1168 ClearElement(ass, "AssemblyCulture");
1170 // Why-oh-why do we put assembly attributes in each type file?
1171 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1172 // since they're outdated in current docs, and a waste of space.
1173 //MakeAttributes(ass, type.Assembly, true);
1174 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1175 if (assattrs != null)
1176 ass.RemoveChild(assattrs);
1178 NormalizeWhitespace(ass);
1180 if (type.IsGenericType ()) {
1181 MakeTypeParameters (root, type.GenericParameters);
1183 ClearElement(root, "TypeParameters");
1186 if (type.BaseType != null) {
1187 XmlElement basenode = WriteElement(root, "Base");
1189 string basetypename = GetDocTypeFullName (type.BaseType);
1190 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1191 WriteElementText(root, "Base/BaseTypeName", basetypename);
1193 // Document how this type instantiates the generic parameters of its base type
1194 TypeReference origBase = type.BaseType.GetElementType ();
1195 if (origBase.IsGenericType ()) {
1196 ClearElement(basenode, "BaseTypeArguments");
1197 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1198 IList<TypeReference> baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1199 IList<GenericParameter> baseGenParams = origBase.GenericParameters;
1200 if (baseGenArgs.Count != baseGenParams.Count)
1201 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1202 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1203 GenericParameter param = baseGenParams [i];
1204 TypeReference value = baseGenArgs [i];
1206 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1207 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1208 bta.AppendChild(arg);
1209 arg.SetAttribute ("TypeParamName", param.Name);
1210 arg.InnerText = GetDocTypeFullName (value);
1214 ClearElement(root, "Base");
1217 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1218 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1219 List<string> interface_names = userInterfaces
1220 .Select (iface => GetDocTypeFullName (iface))
1224 XmlElement interfaces = WriteElement(root, "Interfaces");
1225 interfaces.RemoveAll();
1226 foreach (string iname in interface_names) {
1227 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1228 interfaces.AppendChild(iface);
1229 WriteElementText(iface, "InterfaceName", iname);
1232 ClearElement(root, "Interfaces");
1235 MakeAttributes (root, GetCustomAttributes (type));
1237 if (DocUtils.IsDelegate (type)) {
1238 MakeTypeParameters (root, type.GenericParameters);
1239 MakeParameters(root, type.GetMethod("Invoke").Parameters);
1240 MakeReturnValue(root, type.GetMethod("Invoke"));
1243 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1244 MakeDocNode (typeInfo);
1246 if (!DocUtils.IsDelegate (type))
1247 WriteElement (root, "Members");
1249 OrderTypeNodes (root, root.ChildNodes);
1250 NormalizeWhitespace(root);
1253 static readonly string[] TypeNodeOrder = {
1257 "ThreadingSafetyStatement",
1258 "ThreadSafetyStatement",
1270 static void OrderTypeNodes (XmlNode member, XmlNodeList children)
1272 ReorderNodes (member, children, TypeNodeOrder);
1275 internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1277 List<T> l = new List<T> (list);
1282 private void UpdateMember (DocsNodeInfo info)
1284 XmlElement me = (XmlElement) info.Node;
1285 MemberReference mi = info.Member;
1287 foreach (MemberFormatter f in memberFormatters) {
1288 string element = "MemberSignature[@Language='" + f.Language + "']";
1289 WriteElementAttribute (me, element, "Language", f.Language);
1290 WriteElementAttribute (me, element, "Value", f.GetDeclaration (mi));
1293 WriteElementText(me, "MemberType", GetMemberType(mi));
1295 if (!no_assembly_versions) {
1296 UpdateAssemblyVersions (me, mi, true);
1299 ClearElement (me, "AssemblyInfo");
1302 MakeAttributes (me, GetCustomAttributes (mi));
1304 MakeReturnValue(me, mi);
1305 if (mi is MethodReference) {
1306 MethodReference mb = (MethodReference) mi;
1307 if (mb.IsGenericMethod ())
1308 MakeTypeParameters (me, mb.GenericParameters);
1310 MakeParameters(me, mi);
1313 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1314 WriteElementText(me, "MemberValue", fieldValue);
1316 info.Node = WriteElement (me, "Docs");
1318 OrderMemberNodes (me, me.ChildNodes);
1319 UpdateExtensionMethods (me, info);
1322 static readonly string[] MemberNodeOrder = {
1337 static void OrderMemberNodes (XmlNode member, XmlNodeList children)
1339 ReorderNodes (member, children, MemberNodeOrder);
1342 static void ReorderNodes (XmlNode node, XmlNodeList children, string[] ordering)
1344 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1345 for (int i = 0; i < ordering.Length; ++i) {
1346 for (int j = 0; j < children.Count; ++j) {
1347 XmlNode c = children [j];
1348 if (c.Name == ordering [i]) {
1349 newChildren.Add (c);
1353 if (newChildren.Count >= 0)
1354 node.PrependChild ((XmlNode) newChildren [0]);
1355 for (int i = 1; i < newChildren.Count; ++i) {
1356 XmlNode prev = (XmlNode) newChildren [i-1];
1357 XmlNode cur = (XmlNode) newChildren [i];
1358 node.RemoveChild (cur);
1359 node.InsertAfter (cur, prev);
1363 IEnumerable<string> GetCustomAttributes (MemberReference mi)
1365 IEnumerable<string> attrs = Enumerable.Empty<string>();
1367 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1369 attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1371 PropertyDefinition pd = mi as PropertyDefinition;
1373 if (pd.GetMethod != null)
1374 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1375 if (pd.SetMethod != null)
1376 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1379 EventDefinition ed = mi as EventDefinition;
1381 if (ed.AddMethod != null)
1382 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1383 if (ed.RemoveMethod != null)
1384 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1390 IEnumerable<string> GetCustomAttributes (IList<CustomAttribute> attributes, string prefix)
1392 foreach (CustomAttribute attribute in attributes.OrderBy (ca => ca.AttributeType.FullName)) {
1394 TypeDefinition attrType = attribute.AttributeType as TypeDefinition;
1395 if (attrType != null && !IsPublic (attrType))
1397 if (slashdocFormatter.GetName (attribute.AttributeType) == null)
1400 if (Array.IndexOf (IgnorableAttributes, attribute.AttributeType.FullName) >= 0)
1403 StringList fields = new StringList ();
1405 for (int i = 0; i < attribute.ConstructorArguments.Count; ++i) {
1406 CustomAttributeArgument argument = attribute.ConstructorArguments [i];
1407 fields.Add (MakeAttributesValueString (
1412 (from namedArg in attribute.Fields
1413 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value })
1415 (from namedArg in attribute.Properties
1416 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value }))
1417 .OrderBy (v => v.Name);
1418 foreach (var d in namedArgs)
1419 fields.Add (string.Format ("{0}={1}", d.Name,
1420 MakeAttributesValueString (d.Value, d.Type)));
1422 string a2 = String.Join(", ", fields.ToArray ());
1423 if (a2 != "") a2 = "(" + a2 + ")";
1425 string name = attribute.GetDeclaringType();
1426 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1427 yield return prefix + name + a2;
1431 static readonly string[] ValidExtensionMembers = {
1440 static readonly string[] ValidExtensionDocMembers = {
1446 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1448 MethodDefinition me = info.Member as MethodDefinition;
1451 if (info.Parameters.Count < 1)
1453 if (!DocUtils.IsExtensionMethod (me))
1456 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1457 XmlNode member = e.CloneNode (true);
1458 em.AppendChild (member);
1459 RemoveExcept (member, ValidExtensionMembers);
1460 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1461 WriteElementText (member, "MemberType", "ExtensionMethod");
1462 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1463 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1464 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1465 member.AppendChild (link);
1466 AddTargets (em, info);
1468 extensionMethods.Add (em);
1471 private static void RemoveExcept (XmlNode node, string[] except)
1475 MyXmlNodeList remove = null;
1476 foreach (XmlNode n in node.ChildNodes) {
1477 if (Array.BinarySearch (except, n.Name) < 0) {
1479 remove = new MyXmlNodeList ();
1484 foreach (XmlNode n in remove)
1485 node.RemoveChild (n);
1488 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1490 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1491 member.PrependChild (targets);
1492 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1493 AppendElementAttributeText (targets, "Target", "Type",
1494 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1497 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1498 IList<TypeReference> constraints = gp.Constraints;
1499 if (constraints.Count == 0)
1500 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1502 foreach (TypeReference c in constraints)
1503 AppendElementAttributeText(targets, "Target", "Type",
1504 slashdocFormatter.GetDeclaration (c));
1508 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1511 TypeDefinition type = field.DeclaringType.Resolve ();
1512 if (type != null && type.IsEnum) return false;
1514 if (type != null && type.IsGenericType ()) return false;
1515 if (!field.HasConstant)
1517 if (field.IsLiteral) {
1518 object val = field.Constant;
1519 if (val == null) value = "null";
1520 else if (val is Enum) value = val.ToString();
1521 else if (val is IFormattable) {
1522 value = ((IFormattable)val).ToString();
1524 value = "\"" + value + "\"";
1526 if (value != null && value != "")
1532 // XML HELPER FUNCTIONS
1534 internal static XmlElement WriteElement(XmlNode parent, string element) {
1535 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1537 string[] path = element.Split('/');
1538 foreach (string p in path) {
1539 ret = (XmlElement)parent.SelectSingleNode(p);
1542 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1543 ename = ename.Substring(0, ename.IndexOf('['));
1544 ret = parent.OwnerDocument.CreateElement(ename);
1545 parent.AppendChild(ret);
1554 private static void WriteElementText(XmlNode parent, string element, string value) {
1555 XmlElement node = WriteElement(parent, element);
1556 node.InnerText = value;
1559 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1561 XmlElement n = parent.OwnerDocument.CreateElement (element);
1562 parent.AppendChild (n);
1563 n.InnerText = value;
1567 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1569 XmlElement n = parent.OwnerDocument.CreateElement (element);
1570 parent.AppendChild (n);
1571 n.SetAttribute (attribute, value);
1575 internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
1577 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1578 dest.AppendChild (copy);
1582 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1583 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1586 node = WriteElement(parent, element);
1587 node.InnerText = value;
1589 private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
1590 XmlElement node = WriteElement(parent, element);
1591 if (node.GetAttribute(attribute) == value) return;
1592 node.SetAttribute(attribute, value);
1594 internal static void ClearElement(XmlElement parent, string name) {
1595 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1597 parent.RemoveChild(node);
1600 // DOCUMENTATION HELPER FUNCTIONS
1602 private void MakeDocNode (DocsNodeInfo info)
1604 List<GenericParameter> genericParams = info.GenericParameters;
1605 IList<ParameterDefinition> parameters = info.Parameters;
1606 TypeReference returntype = info.ReturnType;
1607 bool returnisreturn = info.ReturnIsReturn;
1608 XmlElement e = info.Node;
1609 bool addremarks = info.AddRemarks;
1611 WriteElementInitialText(e, "summary", "To be added.");
1613 if (parameters != null) {
1614 string[] values = new string [parameters.Count];
1615 for (int i = 0; i < values.Length; ++i)
1616 values [i] = parameters [i].Name;
1617 UpdateParameters (e, "param", values);
1620 if (genericParams != null) {
1621 string[] values = new string [genericParams.Count];
1622 for (int i = 0; i < values.Length; ++i)
1623 values [i] = genericParams [i].Name;
1624 UpdateParameters (e, "typeparam", values);
1627 string retnodename = null;
1628 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
1629 retnodename = returnisreturn ? "returns" : "value";
1630 string retnodename_other = !returnisreturn ? "returns" : "value";
1632 // If it has a returns node instead of a value node, change its name.
1633 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1634 if (retother != null) {
1635 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1636 foreach (XmlNode node in retother)
1637 retnode.AppendChild(node.CloneNode(true));
1638 e.ReplaceChild(retnode, retother);
1640 WriteElementInitialText(e, retnodename, "To be added.");
1643 ClearElement(e, "returns");
1644 ClearElement(e, "value");
1648 WriteElementInitialText(e, "remarks", "To be added.");
1650 if (exceptions.HasValue && info.Member != null &&
1651 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
1652 UpdateExceptions (e, info.Member);
1655 foreach (DocumentationImporter importer in importers)
1656 importer.ImportDocumentation (info);
1658 OrderDocsNodes (e, e.ChildNodes);
1659 NormalizeWhitespace(e);
1662 static readonly string[] DocsNodeOrder = {
1663 "typeparam", "param", "summary", "returns", "value", "remarks",
1666 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
1668 ReorderNodes (docs, children, DocsNodeOrder);
1672 private void UpdateParameters (XmlElement e, string element, string[] values)
1674 if (values != null) {
1675 XmlNode[] paramnodes = new XmlNode[values.Length];
1677 // Some documentation had param nodes with leading spaces.
1678 foreach (XmlElement paramnode in e.SelectNodes(element)){
1679 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
1682 // If a member has only one parameter, we can track changes to
1683 // the name of the parameter easily.
1684 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
1685 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
1688 bool reinsert = false;
1690 // Pick out existing and still-valid param nodes, and
1691 // create nodes for parameters not in the file.
1692 Hashtable seenParams = new Hashtable();
1693 for (int pi = 0; pi < values.Length; pi++) {
1694 string p = values [pi];
1697 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
1698 if (paramnodes[pi] != null) continue;
1700 XmlElement pe = e.OwnerDocument.CreateElement(element);
1701 pe.SetAttribute("name", p);
1702 pe.InnerText = "To be added.";
1703 paramnodes[pi] = pe;
1707 // Remove parameters that no longer exist and check all params are in the right order.
1709 MyXmlNodeList todelete = new MyXmlNodeList ();
1710 foreach (XmlElement paramnode in e.SelectNodes(element)) {
1711 string name = paramnode.GetAttribute("name");
1712 if (!seenParams.ContainsKey(name)) {
1713 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1714 Warning ("The following param node can only be deleted if the --delete option is given: ");
1715 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
1717 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
1718 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1722 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
1723 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1724 e.ParentNode.Attributes ["MemberName"].Value,
1727 Warning ("\tValue={0}", paramnode.OuterXml);
1729 todelete.Add (paramnode);
1734 if ((int)seenParams[name] != idx)
1740 foreach (XmlNode n in todelete) {
1741 n.ParentNode.RemoveChild (n);
1744 // Re-insert the parameter nodes at the top of the doc section.
1746 for (int pi = values.Length-1; pi >= 0; pi--)
1747 e.PrependChild(paramnodes[pi]);
1749 // Clear all existing param nodes
1750 foreach (XmlNode paramnode in e.SelectNodes(element)) {
1751 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1752 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
1753 Console.WriteLine(paramnode.OuterXml);
1755 paramnode.ParentNode.RemoveChild(paramnode);
1761 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
1763 string existingName = pe.GetAttribute ("name");
1764 pe.SetAttribute ("name", newName);
1765 if (existingName == newName)
1767 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
1768 if (paramref.GetAttribute ("name").Trim () == existingName)
1769 paramref.SetAttribute ("name", newName);
1772 class CrefComparer : XmlNodeComparer {
1774 public CrefComparer ()
1778 public override int Compare (XmlNode x, XmlNode y)
1780 string xType = x.Attributes ["cref"].Value;
1781 string yType = y.Attributes ["cref"].Value;
1782 string xNamespace = GetNamespace (xType);
1783 string yNamespace = GetNamespace (yType);
1785 int c = xNamespace.CompareTo (yNamespace);
1788 return xType.CompareTo (yType);
1791 static string GetNamespace (string type)
1793 int n = type.LastIndexOf ('.');
1795 return type.Substring (0, n);
1796 return string.Empty;
1800 private void UpdateExceptions (XmlNode docs, MemberReference member)
1802 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
1803 string cref = slashdocFormatter.GetDeclaration (source.Exception);
1804 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
1807 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
1808 e.SetAttribute ("cref", cref);
1809 e.InnerXml = "To be added; from: <see cref=\"" +
1810 string.Join ("\" />, <see cref=\"",
1811 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
1814 docs.AppendChild (e);
1816 SortXmlNodes (docs, docs.SelectNodes ("exception"),
1817 new CrefComparer ());
1820 private static void NormalizeWhitespace(XmlElement e) {
1821 // Remove all text and whitespace nodes from the element so it
1822 // is outputted with nice indentation and no blank lines.
1823 ArrayList deleteNodes = new ArrayList();
1824 foreach (XmlNode n in e)
1825 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
1827 foreach (XmlNode n in deleteNodes)
1828 n.ParentNode.RemoveChild(n);
1831 private static bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
1833 TypeDefinition type = member as TypeDefinition;
1835 type = member.DeclaringType as TypeDefinition;
1836 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
1839 private static string GetAssemblyVersion (AssemblyDefinition assembly)
1841 return assembly.Name.Version.ToString();
1844 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
1846 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
1848 e = root.OwnerDocument.CreateElement("AssemblyInfo");
1849 root.AppendChild(e);
1851 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
1852 .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
1854 // matches.Count > 0 && add: ignore -- already present
1855 if (matches.Count > 0 && !add) {
1856 foreach (XmlNode c in matches)
1859 else if (matches.Count == 0 && add) {
1860 foreach (string sv in assemblyVersions) {
1861 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
1866 // matches.Count == 0 && !add: ignore -- already not present
1868 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
1869 SortXmlNodes (e, avs, new VersionComparer ());
1871 return avs.Count != 0;
1874 // FIXME: get TypeReferences instead of string comparison?
1875 private static string[] IgnorableAttributes = {
1876 // Security related attributes
1877 "System.Reflection.AssemblyKeyFileAttribute",
1878 "System.Reflection.AssemblyDelaySignAttribute",
1879 // Present in @RefType
1880 "System.Runtime.InteropServices.OutAttribute",
1881 // For naming the indexer to use when not using indexers
1882 "System.Reflection.DefaultMemberAttribute",
1883 // for decimal constants
1884 "System.Runtime.CompilerServices.DecimalConstantAttribute",
1885 // compiler generated code
1886 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
1887 // more compiler generated code, e.g. iterator methods
1888 "System.Diagnostics.DebuggerHiddenAttribute",
1889 "System.Runtime.CompilerServices.FixedBufferAttribute",
1890 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
1891 // extension methods
1892 "System.Runtime.CompilerServices.ExtensionAttribute",
1893 // Used to differentiate 'object' from C#4 'dynamic'
1894 "System.Runtime.CompilerServices.DynamicAttribute",
1897 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes)
1899 if (!attributes.Any ()) {
1900 ClearElement (root, "Attributes");
1904 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
1908 e = root.OwnerDocument.CreateElement("Attributes");
1910 foreach (string attribute in attributes) {
1911 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
1914 WriteElementText(ae, "AttributeName", attribute);
1917 if (e.ParentNode == null)
1918 root.AppendChild(e);
1920 NormalizeWhitespace(e);
1923 private static string MakeAttributesValueString (object v, TypeReference valueType)
1927 if (valueType.FullName == "System.Type")
1928 return "typeof(" + v.ToString () + ")";
1929 if (valueType.FullName == "System.String")
1930 return "\"" + v.ToString () + "\"";
1932 return (bool)v ? "true" : "false";
1933 TypeDefinition valueDef = valueType.Resolve ();
1934 if (valueDef == null || !valueDef.IsEnum)
1935 return v.ToString ();
1936 string typename = GetDocTypeFullName (valueType);
1937 var values = GetEnumerationValues (valueDef);
1938 long c = ToInt64 (v);
1939 if (values.ContainsKey (c))
1940 return typename + "." + values [c];
1941 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
1942 return string.Join (" | ",
1943 (from i in values.Keys
1945 select typename + "." + values [i])
1948 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
1951 private static Dictionary<long, string> GetEnumerationValues (TypeDefinition type)
1953 var values = new Dictionary<long, string> ();
1955 (from f in type.Fields
1956 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
1958 values [ToInt64 (f.Constant)] = f.Name;
1963 static long ToInt64 (object value)
1966 return (long) (ulong) value;
1967 return Convert.ToInt64 (value);
1970 private void MakeParameters (XmlElement root, IList<ParameterDefinition> parameters)
1972 XmlElement e = WriteElement(root, "Parameters");
1974 foreach (ParameterDefinition p in parameters) {
1975 XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
1977 pe.SetAttribute("Name", p.Name);
1978 pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
1979 if (p.ParameterType is ByReferenceType) {
1980 if (p.IsOut) pe.SetAttribute("RefType", "out");
1981 else pe.SetAttribute("RefType", "ref");
1983 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
1987 private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams)
1989 if (typeParams == null || typeParams.Count == 0) {
1990 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
1992 root.RemoveChild (f);
1995 XmlElement e = WriteElement(root, "TypeParameters");
1997 foreach (GenericParameter t in typeParams) {
1998 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2000 pe.SetAttribute("Name", t.Name);
2001 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""));
2002 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2003 IList<TypeReference> constraints = t.Constraints;
2004 GenericParameterAttributes attrs = t.Attributes;
2005 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2013 ce = root.OwnerDocument.CreateElement ("Constraints");
2015 pe.AppendChild (ce);
2016 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2017 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2018 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2019 AppendElementText (ce, "ParameterAttribute", "Covariant");
2020 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2021 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2022 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2023 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2024 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2025 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2026 foreach (TypeReference c in constraints) {
2027 TypeDefinition cd = c.Resolve ();
2028 AppendElementText (ce,
2029 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2030 GetDocTypeFullName (c));
2035 private void MakeParameters (XmlElement root, MemberReference mi)
2037 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2038 MakeParameters (root, ((MethodDefinition)mi).Parameters);
2039 else if (mi is MethodDefinition) {
2040 MethodDefinition mb = (MethodDefinition) mi;
2041 IList<ParameterDefinition> parameters = mb.Parameters;
2042 MakeParameters(root, parameters);
2043 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2044 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2045 p.SetAttribute ("RefType", "this");
2048 else if (mi is PropertyDefinition) {
2049 IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
2050 if (parameters.Count > 0)
2051 MakeParameters(root, parameters);
2055 else if (mi is FieldDefinition) return;
2056 else if (mi is EventDefinition) return;
2057 else throw new ArgumentException();
2060 internal static string GetDocParameterType (TypeReference type)
2062 return GetDocTypeFullName (type).Replace ("@", "&");
2065 private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes)
2067 XmlElement e = WriteElement(root, "ReturnValue");
2069 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2070 if (attributes != null)
2071 MakeAttributes(e, GetCustomAttributes (attributes, ""));
2074 private void MakeReturnValue (XmlElement root, MemberReference mi)
2076 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2078 else if (mi is MethodDefinition)
2079 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes);
2080 else if (mi is PropertyDefinition)
2081 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
2082 else if (mi is FieldDefinition)
2083 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
2084 else if (mi is EventDefinition)
2085 MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
2087 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2090 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2092 MemberReference mi = info.Member;
2093 if (mi is TypeDefinition) return null;
2095 string sigs = memberFormatters [0].GetDeclaration (mi);
2096 if (sigs == null) return null; // not publicly visible
2098 // no documentation for property/event accessors. Is there a better way of doing this?
2099 if (mi.Name.StartsWith("get_")) return null;
2100 if (mi.Name.StartsWith("set_")) return null;
2101 if (mi.Name.StartsWith("add_")) return null;
2102 if (mi.Name.StartsWith("remove_")) return null;
2103 if (mi.Name.StartsWith("raise_")) return null;
2105 XmlElement me = doc.CreateElement("Member");
2106 me.SetAttribute("MemberName", GetMemberName (mi));
2110 if (exceptions.HasValue &&
2111 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2112 UpdateExceptions (info.Node, info.Member);
2114 if (since != null) {
2115 XmlNode docs = me.SelectSingleNode("Docs");
2116 docs.AppendChild (CreateSinceNode (doc));
2122 internal static string GetMemberName (MemberReference mi)
2124 MethodDefinition mb = mi as MethodDefinition;
2126 PropertyDefinition pi = mi as PropertyDefinition;
2129 return DocUtils.GetPropertyName (pi);
2131 StringBuilder sb = new StringBuilder (mi.Name.Length);
2132 if (!DocUtils.IsExplicitlyImplemented (mb))
2133 sb.Append (mi.Name);
2135 TypeReference iface;
2136 MethodReference ifaceMethod;
2137 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2138 sb.Append (GetDocTypeFullName (iface));
2140 sb.Append (ifaceMethod.Name);
2142 if (mb.IsGenericMethod ()) {
2143 IList<GenericParameter> typeParams = mb.GenericParameters;
2144 if (typeParams.Count > 0) {
2146 sb.Append (typeParams [0].Name);
2147 for (int i = 1; i < typeParams.Count; ++i)
2148 sb.Append (",").Append (typeParams [i].Name);
2152 return sb.ToString ();
2155 /// SIGNATURE GENERATION FUNCTIONS
2156 internal static bool IsPrivate (MemberReference mi)
2158 return memberFormatters [0].GetDeclaration (mi) == null;
2161 internal static string GetMemberType (MemberReference mi)
2163 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2164 return "Constructor";
2165 if (mi is MethodDefinition)
2167 if (mi is PropertyDefinition)
2169 if (mi is FieldDefinition)
2171 if (mi is EventDefinition)
2173 throw new ArgumentException();
2176 private static string GetDocTypeName (TypeReference type)
2178 return docTypeFormatter.GetName (type);
2181 internal static string GetDocTypeFullName (TypeReference type)
2183 return DocTypeFullMemberFormatter.Default.GetName (type);
2186 internal static string GetXPathForMember (DocumentationMember member)
2188 StringBuilder xpath = new StringBuilder ();
2189 xpath.Append ("//Members/Member[@MemberName=\"")
2190 .Append (member.MemberName)
2192 if (member.Parameters != null && member.Parameters.Count > 0) {
2193 xpath.Append ("/Parameters[count(Parameter) = ")
2194 .Append (member.Parameters.Count);
2195 for (int i = 0; i < member.Parameters.Count; ++i) {
2196 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2197 xpath.Append (member.Parameters [i]);
2198 xpath.Append ("\"");
2200 xpath.Append ("]/..");
2202 return xpath.ToString ();
2205 public static string GetXPathForMember (XPathNavigator member)
2207 StringBuilder xpath = new StringBuilder ();
2208 xpath.Append ("//Type[@FullName=\"")
2209 .Append (member.SelectSingleNode ("../../@FullName").Value)
2211 xpath.Append ("Members/Member[@MemberName=\"")
2212 .Append (member.SelectSingleNode ("@MemberName").Value)
2214 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2215 if (parameters.Count > 0) {
2216 xpath.Append ("/Parameters[count(Parameter) = ")
2217 .Append (parameters.Count);
2219 while (parameters.MoveNext ()) {
2221 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2222 xpath.Append (parameters.Current.Value);
2223 xpath.Append ("\"");
2225 xpath.Append ("]/..");
2227 return xpath.ToString ();
2230 public static string GetXPathForMember (MemberReference member)
2232 StringBuilder xpath = new StringBuilder ();
2233 xpath.Append ("//Type[@FullName=\"")
2234 .Append (member.DeclaringType.FullName)
2236 xpath.Append ("Members/Member[@MemberName=\"")
2237 .Append (GetMemberName (member))
2240 IList<ParameterDefinition> parameters = null;
2241 if (member is MethodDefinition)
2242 parameters = ((MethodDefinition) member).Parameters;
2243 else if (member is PropertyDefinition) {
2244 parameters = ((PropertyDefinition) member).Parameters;
2246 if (parameters != null && parameters.Count > 0) {
2247 xpath.Append ("/Parameters[count(Parameter) = ")
2248 .Append (parameters.Count);
2249 for (int i = 0; i < parameters.Count; ++i) {
2250 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2251 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2252 xpath.Append ("\"");
2254 xpath.Append ("]/..");
2256 return xpath.ToString ();
2260 static class CecilExtensions {
2261 public static string GetDeclaringType(this CustomAttribute attribute)
2263 return attribute.Constructor.DeclaringType.FullName;
2266 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
2268 foreach (var c in type.Methods.Where (m => m.IsConstructor))
2269 yield return (MemberReference) c;
2270 foreach (var e in type.Events)
2271 yield return (MemberReference) e;
2272 foreach (var f in type.Fields)
2273 yield return (MemberReference) f;
2274 foreach (var m in type.Methods.Where (m => !m.IsConstructor))
2275 yield return (MemberReference) m;
2276 foreach (var t in type.NestedTypes)
2277 yield return (MemberReference) t;
2278 foreach (var p in type.Properties)
2279 yield return (MemberReference) p;
2282 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
2284 return GetMembers (type).Where (m => m.Name == member);
2287 public static MemberReference GetMember (this TypeDefinition type, string member)
2289 return GetMembers (type, member).EnsureZeroOrOne ();
2292 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2294 if (source.Count () > 1)
2295 throw new InvalidOperationException ("too many matches");
2296 return source.FirstOrDefault ();
2299 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2302 .Where (m => m.Name == method)
2303 .EnsureZeroOrOne ();
2306 public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
2308 TypeDefinition def = type as TypeDefinition;
2310 return new MemberReference [0];
2311 CustomAttribute defMemberAttr = def.CustomAttributes
2312 .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
2313 if (defMemberAttr == null)
2314 return new MemberReference [0];
2315 string name = (string) defMemberAttr.ConstructorArguments [0].Value;
2316 return def.Properties
2317 .Where (p => p.Name == name)
2318 .Select (p => (MemberReference) p);
2321 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2323 return assembly.Modules.SelectMany (md => md.GetAllTypes ());
2326 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2328 return GetTypes (assembly)
2329 .Where (td => td.FullName == type)
2330 .EnsureZeroOrOne ();
2333 public static bool IsGenericType (this TypeReference type)
2335 return type.GenericParameters.Count > 0;
2338 public static bool IsGenericMethod (this MethodReference method)
2340 return method.GenericParameters.Count > 0;
2343 public static MemberReference Resolve (this MemberReference member)
2345 FieldReference fr = member as FieldReference;
2347 return fr.Resolve ();
2348 MethodReference mr = member as MethodReference;
2350 return mr.Resolve ();
2351 TypeReference tr = member as TypeReference;
2353 return tr.Resolve ();
2354 PropertyReference pr = member as PropertyReference;
2357 EventReference er = member as EventReference;
2360 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2363 public static TypeReference GetUnderlyingType (this TypeDefinition type)
2367 return type.Fields.First (f => f.Name == "value__").FieldType;
2370 public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
2372 return self.Types.SelectMany (t => t.GetAllTypes ());
2375 static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
2379 if (!self.HasNestedTypes)
2382 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
2387 static class DocUtils {
2388 public static bool IsExplicitlyImplemented (MethodDefinition method)
2390 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2393 public static string GetTypeDotMember (string name)
2395 int startType, startMethod;
2396 startType = startMethod = -1;
2397 for (int i = 0; i < name.Length; ++i) {
2398 if (name [i] == '.') {
2399 startType = startMethod;
2403 return name.Substring (startType+1);
2406 public static string GetMember (string name)
2408 int i = name.LastIndexOf ('.');
2411 return name.Substring (i+1);
2414 public static void GetInfoForExplicitlyImplementedMethod (
2415 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2419 if (method.Overrides.Count != 1)
2420 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2421 iface = method.Overrides [0].DeclaringType;
2422 ifaceMethod = method.Overrides [0];
2425 public static string GetPropertyName (PropertyDefinition pi)
2427 // Issue: (g)mcs-generated assemblies that explicitly implement
2428 // properties don't specify the full namespace, just the
2429 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2430 MethodDefinition method = pi.GetMethod;
2432 method = pi.SetMethod;
2433 if (!IsExplicitlyImplemented (method))
2436 // Need to determine appropriate namespace for this member.
2437 TypeReference iface;
2438 MethodReference ifaceMethod;
2439 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2440 return string.Join (".", new string[]{
2441 DocTypeFullMemberFormatter.Default.GetName (iface),
2442 GetMember (pi.Name)});
2445 public static string GetNamespace (TypeReference type)
2447 if (type.GetElementType ().IsNested)
2448 type = type.GetElementType ();
2449 while (type != null && type.IsNested)
2450 type = type.DeclaringType;
2452 return string.Empty;
2453 return type.Namespace;
2456 public static string PathCombine (string dir, string path)
2462 return Path.Combine (dir, path);
2465 public static bool IsExtensionMethod (MethodDefinition method)
2468 method.CustomAttributes
2469 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2470 && method.DeclaringType.CustomAttributes
2471 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
2474 public static bool IsDelegate (TypeDefinition type)
2476 TypeReference baseRef = type.BaseType;
2477 if (baseRef == null)
2479 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2480 baseRef.FullName == "System.MulticastDelegate";
2483 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2485 List<TypeReference> decls = new List<TypeReference> ();
2487 while (type.DeclaringType != null) {
2488 decls.Add (type.DeclaringType);
2489 type = type.DeclaringType;
2495 public static int GetGenericArgumentCount (TypeReference type)
2497 GenericInstanceType inst = type as GenericInstanceType;
2499 ? inst.GenericArguments.Count
2500 : type.GenericParameters.Count;
2503 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
2505 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
2506 List<TypeReference> userInterfaces = new List<TypeReference> ();
2507 foreach (TypeReference iface in type.Interfaces) {
2508 TypeReference lookup = iface.Resolve () ?? iface;
2509 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
2510 userInterfaces.Add (iface);
2512 return userInterfaces;
2515 private static string GetQualifiedTypeName (TypeReference type)
2517 return "[" + type.Scope.Name + "]" + type.FullName;
2520 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
2522 HashSet<string> inheritedInterfaces = new HashSet<string> ();
2523 Action<TypeDefinition> a = null;
2525 if (t == null) return;
2526 foreach (TypeReference r in t.Interfaces) {
2527 inheritedInterfaces.Add (GetQualifiedTypeName (r));
2531 TypeReference baseRef = type.BaseType;
2532 while (baseRef != null) {
2533 TypeDefinition baseDef = baseRef.Resolve ();
2534 if (baseDef != null) {
2536 baseRef = baseDef.BaseType;
2541 foreach (TypeReference r in type.Interfaces)
2543 return inheritedInterfaces;
2547 class DocsNodeInfo {
2548 public DocsNodeInfo (XmlElement node)
2553 public DocsNodeInfo (XmlElement node, TypeDefinition type)
2559 public DocsNodeInfo (XmlElement node, MemberReference member)
2562 SetMemberInfo (member);
2565 void SetType (TypeDefinition type)
2568 throw new ArgumentNullException ("type");
2570 GenericParameters = new List<GenericParameter> (type.GenericParameters);
2571 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
2572 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
2573 for (int i = 0; i < declTypes.Count - 1; ++i) {
2574 int remove = System.Math.Min (maxGenArgs,
2575 DocUtils.GetGenericArgumentCount (declTypes [i]));
2576 maxGenArgs -= remove;
2577 while (remove-- > 0)
2578 GenericParameters.RemoveAt (0);
2580 if (DocUtils.IsDelegate (type)) {
2581 Parameters = type.GetMethod("Invoke").Parameters;
2582 ReturnType = type.GetMethod("Invoke").ReturnType;
2583 ReturnIsReturn = true;
2587 void SetMemberInfo (MemberReference member)
2590 throw new ArgumentNullException ("member");
2591 ReturnIsReturn = true;
2595 if (member is MethodReference ) {
2596 MethodReference mr = (MethodReference) member;
2597 Parameters = mr.Parameters;
2598 if (mr.IsGenericMethod ()) {
2599 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
2602 else if (member is PropertyDefinition) {
2603 Parameters = ((PropertyDefinition) member).Parameters;
2606 if (member is MethodDefinition) {
2607 ReturnType = ((MethodDefinition) member).ReturnType;
2608 } else if (member is PropertyDefinition) {
2609 ReturnType = ((PropertyDefinition) member).PropertyType;
2610 ReturnIsReturn = false;
2613 // no remarks section for enum members
2614 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
2618 public TypeReference ReturnType;
2619 public List<GenericParameter> GenericParameters;
2620 public IList<ParameterDefinition> Parameters;
2621 public bool ReturnIsReturn;
2622 public XmlElement Node;
2623 public bool AddRemarks = true;
2624 public MemberReference Member;
2625 public TypeDefinition Type;
2628 class DocumentationEnumerator {
2630 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2632 return GetDocumentationTypes (assembly, forTypes, null);
2635 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2637 foreach (TypeDefinition type in assembly.GetTypes()) {
2638 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
2640 if (seen != null && seen.Contains (type.FullName))
2643 foreach (TypeDefinition nested in type.NestedTypes)
2644 yield return nested;
2648 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2650 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
2651 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
2652 oldmember.RemoveAttribute ("__monodocer-seen__");
2655 MemberReference m = GetMember (type, new DocumentationMember (oldmember));
2657 yield return new DocsNodeInfo (oldmember);
2660 yield return new DocsNodeInfo (oldmember, m);
2665 protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
2667 string membertype = member.MemberType;
2669 string returntype = member.ReturnType;
2671 string docName = member.MemberName;
2672 string[] docTypeParams = GetTypeParameters (docName);
2674 // Loop through all members in this type with the same name
2675 foreach (MemberReference mi in GetReflectionMembers (type, docName)) {
2676 if (mi is TypeDefinition) continue;
2677 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
2679 if (MDocUpdater.IsPrivate (mi))
2682 IList<ParameterDefinition> pis = null;
2683 string[] typeParams = null;
2684 if (mi is MethodDefinition) {
2685 MethodDefinition mb = (MethodDefinition) mi;
2686 pis = mb.Parameters;
2687 if (docTypeParams != null && mb.IsGenericMethod ()) {
2688 IList<GenericParameter> args = mb.GenericParameters;
2689 if (args.Count == docTypeParams.Length) {
2690 typeParams = args.Select (p => p.Name).ToArray ();
2694 else if (mi is PropertyDefinition)
2695 pis = ((PropertyDefinition)mi).Parameters;
2697 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
2698 int pcount = pis == null ? 0 : pis.Count;
2699 if (mcount != pcount)
2702 MethodDefinition mDef = mi as MethodDefinition;
2703 if (mDef != null && !mDef.IsConstructor) {
2704 // Casting operators can overload based on return type.
2705 if (returntype != GetReplacedString (
2706 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
2707 typeParams, docTypeParams)) {
2715 for (int i = 0; i < pis.Count; i++) {
2716 string paramType = GetReplacedString (
2717 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
2718 typeParams, docTypeParams);
2719 if (paramType != (string) member.Parameters [i]) {
2724 if (!good) continue;
2732 static string[] GetTypeParameters (string docName)
2734 if (docName [docName.Length-1] != '>')
2736 StringList types = new StringList ();
2737 int endToken = docName.Length-2;
2738 int i = docName.Length-2;
2740 if (docName [i] == ',' || docName [i] == '<') {
2741 types.Add (docName.Substring (i + 1, endToken - i));
2744 if (docName [i] == '<')
2749 return types.ToArray ();
2752 protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
2754 // need to worry about 4 forms of //@MemberName values:
2755 // 1. "Normal" (non-generic) member names: GetEnumerator
2757 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
2758 // - try as-is, and try type.member (due to "kludge" for property
2760 // 3. "Normal" Generic member names: Sort<T> (CSC)
2761 // - need to remove generic parameters --> "Sort"
2762 // 4. Explicitly-implemented interface members for generic interfaces:
2763 // -- System.Collections.Generic.IEnumerable<T>.Current
2764 // - Try as-is, and try type.member, *keeping* the generic parameters.
2765 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
2766 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
2767 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
2768 // this as (1) or (2).
2769 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
2771 foreach (MemberReference mi in type.GetMembers (docName))
2773 if (CountChars (docName, '.') > 0)
2774 // might be a property; try only type.member instead of
2775 // namespace.type.member.
2776 foreach (MemberReference mi in
2777 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
2784 int startLt, startType, startMethod;
2785 startLt = startType = startMethod = -1;
2786 for (int i = 0; i < docName.Length; ++i) {
2787 switch (docName [i]) {
2796 if (numLt == 0 && (i + 1) < docName.Length)
2797 // there's another character in docName, so this <...> sequence is
2798 // probably part of a generic type -- case 4.
2802 startType = startMethod;
2808 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
2810 foreach (MemberReference mi in type.GetMembers (refName))
2814 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
2817 // If we _still_ haven't found it, we've hit another generic naming issue:
2818 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
2819 // explicitly-implemented METHOD names (not properties), e.g.
2820 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
2821 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
2822 // which the XML docs will contain.
2824 // Alas, we can't derive the Mono name from docName, so we need to iterate
2825 // over all member names, convert them into CSC format, and compare... :-(
2828 foreach (MemberReference mi in type.GetMembers ()) {
2829 if (MDocUpdater.GetMemberName (mi) == docName)
2834 static string GetReplacedString (string typeName, string[] from, string[] to)
2838 for (int i = 0; i < from.Length; ++i)
2839 typeName = typeName.Replace (from [i], to [i]);
2843 private static int CountChars (string s, char c)
2846 for (int i = 0; i < s.Length; ++i) {
2854 class EcmaDocumentationEnumerator : DocumentationEnumerator {
2859 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
2862 this.ecmadocs = ecmaDocs;
2865 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2867 HashSet<string> seen = new HashSet<string> ();
2868 return GetDocumentationTypes (assembly, forTypes, seen)
2869 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
2872 new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2875 while (ecmadocs.Read ()) {
2876 switch (ecmadocs.Name) {
2878 if (typeDepth == -1)
2879 typeDepth = ecmadocs.Depth;
2880 if (ecmadocs.NodeType != XmlNodeType.Element)
2882 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
2884 string typename = ecmadocs.GetAttribute ("FullName");
2885 string typename2 = MDocUpdater.GetTypeFileName (typename);
2886 if (forTypes != null &&
2887 forTypes.BinarySearch (typename) < 0 &&
2888 typename != typename2 &&
2889 forTypes.BinarySearch (typename2) < 0)
2892 if ((t = assembly.GetType (typename)) == null &&
2893 (t = assembly.GetType (typename2)) == null)
2895 seen.Add (typename);
2896 if (typename != typename2)
2897 seen.Add (typename2);
2898 Console.WriteLine (" Import: {0}", t.FullName);
2899 if (ecmadocs.Name != "Docs") {
2900 int depth = ecmadocs.Depth;
2901 while (ecmadocs.Read ()) {
2902 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
2906 if (!ecmadocs.IsStartElement ("Docs"))
2907 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
2917 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2919 return GetMembers (basefile, type)
2920 .Concat (base.GetDocumentationMembers (basefile, type));
2923 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
2925 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
2928 if (ecmadocs.IsEmptyElement)
2931 int membersDepth = ecmadocs.Depth;
2933 while (go && ecmadocs.Read ()) {
2934 switch (ecmadocs.Name) {
2936 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
2938 DocumentationMember dm = new DocumentationMember (ecmadocs);
2939 string xp = MDocUpdater.GetXPathForMember (dm);
2940 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
2942 if (oldmember == null) {
2943 m = GetMember (type, dm);
2945 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2946 type.FullName, dm.MemberSignatures ["C#"]);
2947 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
2950 // oldmember lookup may have failed due to type parameter renames.
2952 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
2953 if (oldmember == null) {
2954 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
2955 oldmember = basefile.CreateElement ("Member");
2956 oldmember.SetAttribute ("MemberName", dm.MemberName);
2957 members.AppendChild (oldmember);
2958 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
2959 XmlElement ms = basefile.CreateElement ("MemberSignature");
2960 ms.SetAttribute ("Language", key);
2961 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
2962 oldmember.AppendChild (ms);
2964 oldmember.SetAttribute ("__monodocer-seen__", "true");
2965 Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
2970 m = GetMember (type, new DocumentationMember (oldmember));
2972 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2973 type.FullName, dm.MemberSignatures ["C#"]);
2976 oldmember.SetAttribute ("__monodocer-seen__", "true");
2978 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
2979 if (ecmadocs.Name != "Docs")
2980 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
2985 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
2994 abstract class DocumentationImporter {
2996 public abstract void ImportDocumentation (DocsNodeInfo info);
2999 class MsxdocDocumentationImporter : DocumentationImporter {
3001 XmlDocument slashdocs;
3003 public MsxdocDocumentationImporter (string file)
3005 var xml = File.ReadAllText (file);
3007 // Ensure Unix line endings
3008 xml = xml.Replace ("\r", "");
3010 slashdocs = new XmlDocument();
3011 slashdocs.LoadXml (xml);
3014 public override void ImportDocumentation (DocsNodeInfo info)
3016 XmlNode elem = GetDocs (info.Member ?? info.Type);
3021 XmlElement e = info.Node;
3023 if (elem.SelectSingleNode("summary") != null)
3024 MDocUpdater.ClearElement(e, "summary");
3025 if (elem.SelectSingleNode("remarks") != null)
3026 MDocUpdater.ClearElement(e, "remarks");
3027 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3028 MDocUpdater.ClearElement(e, "value");
3029 MDocUpdater.ClearElement(e, "returns");
3032 foreach (XmlNode child in elem.ChildNodes) {
3033 switch (child.Name) {
3036 XmlAttribute name = child.Attributes ["name"];
3039 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3041 p2.InnerXml = child.InnerXml;
3044 // Occasionally XML documentation will use <returns/> on
3045 // properties, so let's try to normalize things.
3048 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3049 v.InnerXml = child.InnerXml;
3055 case "permission": {
3056 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3059 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3061 a = e.OwnerDocument.CreateElement (child.Name);
3062 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
3065 a.InnerXml = child.InnerXml;
3069 XmlAttribute cref = child.Attributes ["cref"];
3072 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3074 a = e.OwnerDocument.CreateElement ("altmember");
3075 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
3082 if (child.NodeType == XmlNodeType.Element &&
3083 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3086 MDocUpdater.CopyNode (child, e);
3093 private XmlNode GetDocs (MemberReference member)
3095 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3096 if (slashdocsig != null)
3097 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3102 class EcmaDocumentationImporter : DocumentationImporter {
3106 public EcmaDocumentationImporter (XmlReader ecmaDocs)
3108 this.ecmadocs = ecmaDocs;
3111 public override void ImportDocumentation (DocsNodeInfo info)
3113 if (!ecmadocs.IsStartElement ("Docs")) {
3117 XmlElement e = info.Node;
3119 int depth = ecmadocs.Depth;
3120 ecmadocs.ReadStartElement ("Docs");
3121 while (ecmadocs.Read ()) {
3122 if (ecmadocs.Name == "Docs") {
3123 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3126 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3128 if (!ecmadocs.IsStartElement ())
3130 switch (ecmadocs.Name) {
3133 string name = ecmadocs.GetAttribute ("name");
3136 XmlNode doc = e.SelectSingleNode (
3137 ecmadocs.Name + "[@name='" + name + "']");
3138 string value = ecmadocs.ReadInnerXml ();
3140 doc.InnerXml = value.Replace ("\r", "");
3147 string name = ecmadocs.Name;
3148 string cref = ecmadocs.GetAttribute ("cref");
3151 XmlNode doc = e.SelectSingleNode (
3152 ecmadocs.Name + "[@cref='" + cref + "']");
3153 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3155 doc.InnerXml = value;
3157 XmlElement n = e.OwnerDocument.CreateElement (name);
3158 n.SetAttribute ("cref", cref);
3165 string name = ecmadocs.Name;
3166 string xpath = ecmadocs.Name;
3167 StringList attributes = new StringList (ecmadocs.AttributeCount);
3168 if (ecmadocs.MoveToFirstAttribute ()) {
3170 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3171 } while (ecmadocs.MoveToNextAttribute ());
3172 ecmadocs.MoveToContent ();
3174 if (attributes.Count > 0) {
3175 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3177 XmlNode doc = e.SelectSingleNode (xpath);
3178 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3180 doc.InnerXml = value;
3183 XmlElement n = e.OwnerDocument.CreateElement (name);
3185 foreach (string a in attributes) {
3186 int eq = a.IndexOf ('=');
3187 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3198 class DocumentationMember {
3199 public StringToStringMap MemberSignatures = new StringToStringMap ();
3200 public string ReturnType;
3201 public StringList Parameters;
3202 public string MemberName;
3203 public string MemberType;
3205 public DocumentationMember (XmlReader reader)
3207 MemberName = reader.GetAttribute ("MemberName");
3208 int depth = reader.Depth;
3210 StringList p = new StringList ();
3212 if (reader.NodeType != XmlNodeType.Element)
3214 switch (reader.Name) {
3215 case "MemberSignature":
3216 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3219 MemberType = reader.ReadElementString ();
3222 if (reader.Depth == depth + 2)
3223 ReturnType = reader.ReadElementString ();
3226 if (reader.Depth == depth + 2)
3227 p.Add (reader.GetAttribute ("Type"));
3230 if (reader.Depth == depth + 1)
3234 } while (go && reader.Read () && reader.Depth >= depth);
3240 public DocumentationMember (XmlNode node)
3242 MemberName = node.Attributes ["MemberName"].Value;
3243 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3244 XmlAttribute l = n.Attributes ["Language"];
3245 XmlAttribute v = n.Attributes ["Value"];
3246 if (l != null && v != null)
3247 MemberSignatures [l.Value] = v.Value;
3249 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3250 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
3252 ReturnType = rt.InnerText;
3253 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
3255 Parameters = new StringList (p.Count);
3256 for (int i = 0; i < p.Count; ++i)
3257 Parameters.Add (p [i].Attributes ["Type"].Value);
3262 public enum MemberFormatterState {
3264 WithinGenericTypeParameters,
3267 public abstract class MemberFormatter {
3269 public virtual string Language {
3273 public virtual string GetName (MemberReference member)
3275 TypeReference type = member as TypeReference;
3277 return GetTypeName (type);
3278 MethodReference method = member as MethodReference;
3279 if (method != null && method.Name == ".ctor") // method.IsConstructor
3280 return GetConstructorName (method);
3282 return GetMethodName (method);
3283 PropertyReference prop = member as PropertyReference;
3285 return GetPropertyName (prop);
3286 FieldReference field = member as FieldReference;
3288 return GetFieldName (field);
3289 EventReference e = member as EventReference;
3291 return GetEventName (e);
3292 throw new NotSupportedException ("Can't handle: " +
3293 (member == null ? "null" : member.GetType().ToString()));
3296 protected virtual string GetTypeName (TypeReference type)
3299 throw new ArgumentNullException ("type");
3300 return _AppendTypeName (new StringBuilder (type.Name.Length), type).ToString ();
3303 protected virtual char[] ArrayDelimeters {
3304 get {return new char[]{'[', ']'};}
3307 protected virtual MemberFormatterState MemberFormatterState { get; set; }
3309 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type)
3311 if (type is ArrayType) {
3312 TypeSpecification spec = type as TypeSpecification;
3313 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType ());
3314 return AppendArrayModifiers (buf, (ArrayType) type);
3316 if (type is ByReferenceType) {
3317 return AppendRefTypeName (buf, type);
3319 if (type is PointerType) {
3320 return AppendPointerTypeName (buf, type);
3322 AppendNamespace (buf, type);
3323 if (type is GenericParameter) {
3324 return AppendTypeName (buf, type);
3326 GenericInstanceType genInst = type as GenericInstanceType;
3327 if (type.GenericParameters.Count == 0 &&
3328 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3329 return AppendFullTypeName (buf, type);
3331 return AppendGenericType (buf, type);
3334 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3336 string ns = DocUtils.GetNamespace (type);
3337 if (ns != null && ns.Length > 0)
3338 buf.Append (ns).Append ('.');
3342 protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type)
3344 if (type.DeclaringType != null)
3345 AppendFullTypeName (buf, type.DeclaringType).Append (NestedTypeSeparator);
3346 return AppendTypeName (buf, type);
3349 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3351 return AppendTypeName (buf, type.Name);
3354 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3356 int n = typename.IndexOf ("`");
3358 return buf.Append (typename.Substring (0, n));
3359 return buf.Append (typename);
3362 protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
3364 buf.Append (ArrayDelimeters [0]);
3365 int rank = array.Rank;
3367 buf.Append (new string (',', rank-1));
3368 return buf.Append (ArrayDelimeters [1]);
3371 protected virtual string RefTypeModifier {
3375 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type)
3377 TypeSpecification spec = type as TypeSpecification;
3378 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType ())
3379 .Append (RefTypeModifier);
3382 protected virtual string PointerModifier {
3386 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type)
3388 TypeSpecification spec = type as TypeSpecification;
3389 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType ())
3390 .Append (PointerModifier);
3393 protected virtual char[] GenericTypeContainer {
3394 get {return new char[]{'<', '>'};}
3397 protected virtual char NestedTypeSeparator {
3401 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3403 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3404 type is GenericInstanceType ? type.GetElementType () : type);
3405 List<TypeReference> genArgs = GetGenericArguments (type);
3408 bool insertNested = false;
3409 foreach (var decl in decls) {
3410 TypeReference declDef = decl.Resolve () ?? decl;
3412 buf.Append (NestedTypeSeparator);
3414 insertNested = true;
3415 AppendTypeName (buf, declDef);
3416 int ac = DocUtils.GetGenericArgumentCount (declDef);
3420 buf.Append (GenericTypeContainer [0]);
3421 var origState = MemberFormatterState;
3422 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3423 _AppendTypeName (buf, genArgs [argIdx++]);
3424 for (int i = 1; i < c; ++i)
3425 _AppendTypeName (buf.Append (","), genArgs [argIdx++]);
3426 MemberFormatterState = origState;
3427 buf.Append (GenericTypeContainer [1]);
3433 protected List<TypeReference> GetGenericArguments (TypeReference type)
3435 var args = new List<TypeReference> ();
3436 GenericInstanceType inst = type as GenericInstanceType;
3438 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
3440 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
3444 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3449 protected virtual string GetConstructorName (MethodReference constructor)
3451 return constructor.Name;
3454 protected virtual string GetMethodName (MethodReference method)
3459 protected virtual string GetPropertyName (PropertyReference property)
3461 return property.Name;
3464 protected virtual string GetFieldName (FieldReference field)
3469 protected virtual string GetEventName (EventReference e)
3474 public virtual string GetDeclaration (MemberReference member)
3477 throw new ArgumentNullException ("member");
3478 TypeDefinition type = member as TypeDefinition;
3480 return GetTypeDeclaration (type);
3481 MethodDefinition method = member as MethodDefinition;
3482 if (method != null && method.IsConstructor)
3483 return GetConstructorDeclaration (method);
3485 return GetMethodDeclaration (method);
3486 PropertyDefinition prop = member as PropertyDefinition;
3488 return GetPropertyDeclaration (prop);
3489 FieldDefinition field = member as FieldDefinition;
3491 return GetFieldDeclaration (field);
3492 EventDefinition e = member as EventDefinition;
3494 return GetEventDeclaration (e);
3495 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3498 protected virtual string GetTypeDeclaration (TypeDefinition type)
3501 throw new ArgumentNullException ("type");
3502 StringBuilder buf = new StringBuilder (type.Name.Length);
3503 _AppendTypeName (buf, type);
3504 AppendGenericTypeConstraints (buf, type);
3505 return buf.ToString ();
3508 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
3510 return GetConstructorName (constructor);
3513 protected virtual string GetMethodDeclaration (MethodDefinition method)
3515 if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
3516 ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
3519 // Special signature for destructors.
3520 if (method.Name == "Finalize" && method.Parameters.Count == 0)
3521 return GetFinalizerName (method);
3523 StringBuilder buf = new StringBuilder ();
3525 AppendVisibility (buf, method);
3526 if (buf.Length == 0 &&
3527 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3530 AppendModifiers (buf, method);
3532 if (buf.Length != 0)
3534 buf.Append (GetTypeName (method.MethodReturnType)).Append (" ");
3536 AppendMethodName (buf, method);
3537 AppendGenericMethod (buf, method).Append (" ");
3538 AppendParameters (buf, method, method.Parameters);
3539 AppendGenericMethodConstraints (buf, method);
3540 return buf.ToString ();
3543 protected virtual string GetTypeName (MethodReturnType returnType)
3545 return GetName (returnType.ReturnType);
3548 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3550 return buf.Append (method.Name);
3553 protected virtual string GetFinalizerName (MethodDefinition method)
3558 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3563 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3568 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3573 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
3578 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3583 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
3585 return GetPropertyName (property);
3588 protected virtual string GetFieldDeclaration (FieldDefinition field)
3590 return GetFieldName (field);
3593 protected virtual string GetEventDeclaration (EventDefinition e)
3595 return GetEventName (e);
3599 class ILFullMemberFormatter : MemberFormatter {
3601 public override string Language {
3602 get {return "ILAsm";}
3605 protected override char NestedTypeSeparator {
3611 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3613 if (GetBuiltinType (type.FullName) != null)
3615 string ns = DocUtils.GetNamespace (type);
3616 if (ns != null && ns.Length > 0) {
3617 if (type.IsValueType)
3618 buf.Append ("valuetype ");
3620 buf.Append ("class ");
3621 buf.Append (ns).Append ('.');
3626 private static string GetBuiltinType (string t)
3629 case "System.Byte": return "unsigned int8";
3630 case "System.SByte": return "int8";
3631 case "System.Int16": return "int16";
3632 case "System.Int32": return "int32";
3633 case "System.Int64": return "int64";
3634 case "System.IntPtr": return "native int";
3636 case "System.UInt16": return "unsigned int16";
3637 case "System.UInt32": return "unsigned int32";
3638 case "System.UInt64": return "unsigned int64";
3639 case "System.UIntPtr": return "native unsigned int";
3641 case "System.Single": return "float32";
3642 case "System.Double": return "float64";
3643 case "System.Boolean": return "bool";
3644 case "System.Char": return "char";
3645 case "System.Void": return "void";
3646 case "System.String": return "string";
3647 case "System.Object": return "object";
3652 protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
3654 return buf.Append (typename);
3657 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3659 if (type is GenericParameter)
3660 return AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
3662 string s = GetBuiltinType (type.FullName);
3664 return buf.Append (s);
3665 return base.AppendTypeName (buf, type);
3668 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
3670 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
3671 return buf.Append (type.Owner is TypeReference ? "!" : "!!");
3673 GenericParameterAttributes attrs = type.Attributes;
3674 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
3675 buf.Append ("class ");
3676 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
3677 buf.Append ("struct ");
3678 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
3679 buf.Append (".ctor ");
3680 IList<TypeReference> constraints = type.Constraints;
3681 MemberFormatterState = 0;
3682 if (constraints.Count > 0) {
3683 var full = new ILFullMemberFormatter ();
3684 buf.Append ("(").Append (full.GetName (constraints [0]));
3685 for (int i = 1; i < constraints.Count; ++i) {
3686 buf.Append (", ").Append (full.GetName (constraints [i]));
3690 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3692 if ((attrs & GenericParameterAttributes.Covariant) != 0)
3694 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
3699 protected override string GetTypeDeclaration (TypeDefinition type)
3701 string visibility = GetTypeVisibility (type.Attributes);
3702 if (visibility == null)
3705 StringBuilder buf = new StringBuilder ();
3707 buf.Append (".class ");
3709 buf.Append ("nested ");
3710 buf.Append (visibility).Append (" ");
3711 if (type.IsInterface)
3712 buf.Append ("interface ");
3713 if (type.IsSequentialLayout)
3714 buf.Append ("sequential ");
3715 if (type.IsAutoLayout)
3716 buf.Append ("auto ");
3717 if (type.IsAnsiClass)
3718 buf.Append ("ansi ");
3719 if (type.IsAbstract)
3720 buf.Append ("abstract ");
3721 if (type.IsSerializable)
3722 buf.Append ("serializable ");
3724 buf.Append ("sealed ");
3725 if (type.IsBeforeFieldInit)
3726 buf.Append ("beforefieldinit ");
3727 var state = MemberFormatterState;
3728 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3729 buf.Append (GetName (type));
3730 MemberFormatterState = state;
3731 var full = new ILFullMemberFormatter ();
3732 if (type.BaseType != null) {
3733 buf.Append (" extends ");
3734 if (type.BaseType.FullName == "System.Object")
3735 buf.Append ("System.Object");
3737 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
3740 foreach (var name in type.Interfaces
3741 .Select (i => full.GetName (i))
3742 .OrderBy (n => n)) {
3744 buf.Append (" implements ");
3753 return buf.ToString ();
3756 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3758 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3759 type is GenericInstanceType ? type.GetElementType () : type);
3761 foreach (var decl in decls) {
3762 TypeReference declDef = decl.Resolve () ?? decl;
3764 buf.Append (NestedTypeSeparator);
3767 AppendTypeName (buf, declDef);
3771 foreach (TypeReference arg in GetGenericArguments (type)) {
3775 _AppendTypeName (buf, arg);
3781 static string GetTypeVisibility (TypeAttributes ta)
3783 switch (ta & TypeAttributes.VisibilityMask) {
3784 case TypeAttributes.Public:
3785 case TypeAttributes.NestedPublic:
3788 case TypeAttributes.NestedFamily:
3789 case TypeAttributes.NestedFamORAssem:
3797 protected override string GetConstructorDeclaration (MethodDefinition constructor)
3799 return GetMethodDeclaration (constructor);
3802 protected override string GetMethodDeclaration (MethodDefinition method)
3804 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
3807 var buf = new StringBuilder ();
3808 buf.Append (".method ");
3809 AppendVisibility (buf, method);
3810 if (method.IsStatic)
3811 buf.Append ("static ");
3812 if (method.IsHideBySig)
3813 buf.Append ("hidebysig ");
3814 if (method.IsPInvokeImpl) {
3815 var info = method.PInvokeInfo;
3816 buf.Append ("pinvokeimpl (\"")
3817 .Append (info.Module.Name)
3818 .Append ("\" as \"")
3819 .Append (info.EntryPoint)
3821 if (info.IsCharSetAuto)
3822 buf.Append (" auto");
3823 if (info.IsCharSetUnicode)
3824 buf.Append (" unicode");
3825 if (info.IsCharSetAnsi)
3826 buf.Append (" ansi");
3827 if (info.IsCallConvCdecl)
3828 buf.Append (" cdecl");
3829 if (info.IsCallConvStdCall)
3830 buf.Append (" stdcall");
3831 if (info.IsCallConvWinapi)
3832 buf.Append (" winapi");
3833 if (info.IsCallConvThiscall)
3834 buf.Append (" thiscall");
3835 if (info.SupportsLastError)
3836 buf.Append (" lasterr");
3839 if (method.IsSpecialName)
3840 buf.Append ("specialname ");
3841 if (method.IsRuntimeSpecialName)
3842 buf.Append ("rtspecialname ");
3843 if (method.IsNewSlot)
3844 buf.Append ("newslot ");
3845 if (method.IsVirtual)
3846 buf.Append ("virtual ");
3847 if (!method.IsStatic)
3848 buf.Append ("instance ");
3849 _AppendTypeName (buf, method.ReturnType);
3851 .Append (method.Name);
3852 if (method.IsGenericMethod ()) {
3853 var state = MemberFormatterState;
3854 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3855 IList<GenericParameter> args = method.GenericParameters;
3856 if (args.Count > 0) {
3858 _AppendTypeName (buf, args [0]);
3859 for (int i = 1; i < args.Count; ++i)
3860 _AppendTypeName (buf.Append (", "), args [i]);
3863 MemberFormatterState = state;
3868 for (int i = 0; i < method.Parameters.Count; ++i) {
3872 _AppendTypeName (buf, method.Parameters [i].ParameterType);
3874 buf.Append (method.Parameters [i].Name);
3878 buf.Append (" cil");
3879 if (method.IsRuntime)
3880 buf.Append (" runtime");
3881 if (method.IsManaged)
3882 buf.Append (" managed");
3884 return buf.ToString ();
3887 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3889 if (DocUtils.IsExplicitlyImplemented (method)) {
3890 TypeReference iface;
3891 MethodReference ifaceMethod;
3892 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3893 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3895 .Append (ifaceMethod.Name);
3897 return base.AppendMethodName (buf, method);
3900 protected override string RefTypeModifier {
3904 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3906 if (method.IsPublic)
3907 return buf.Append ("public ");
3908 if (method.IsFamilyAndAssembly)
3909 return buf.Append ("familyandassembly");
3910 if (method.IsFamilyOrAssembly)
3911 return buf.Append ("familyorassembly");
3912 if (method.IsFamily)
3913 return buf.Append ("family");
3917 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3919 string modifiers = String.Empty;
3920 if (method.IsStatic) modifiers += " static";
3921 if (method.IsVirtual && !method.IsAbstract) {
3922 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3923 else modifiers += " override";
3925 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
3926 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
3927 if (method.IsFinal) modifiers += " sealed";
3928 if (modifiers == " virtual sealed") modifiers = "";
3930 return buf.Append (modifiers);
3933 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3935 if (method.IsGenericMethod ()) {
3936 IList<GenericParameter> args = method.GenericParameters;
3937 if (args.Count > 0) {
3939 buf.Append (args [0].Name);
3940 for (int i = 1; i < args.Count; ++i)
3941 buf.Append (",").Append (args [i].Name);
3948 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
3950 return AppendParameters (buf, method, parameters, '(', ')');
3953 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
3957 if (parameters.Count > 0) {
3958 if (DocUtils.IsExtensionMethod (method))
3959 buf.Append ("this ");
3960 AppendParameter (buf, parameters [0]);
3961 for (int i = 1; i < parameters.Count; ++i) {
3963 AppendParameter (buf, parameters [i]);
3967 return buf.Append (end);
3970 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
3972 if (parameter.ParameterType is ByReferenceType) {
3973 if (parameter.IsOut)
3974 buf.Append ("out ");
3976 buf.Append ("ref ");
3978 buf.Append (GetName (parameter.ParameterType)).Append (" ");
3979 return buf.Append (parameter.Name);
3982 protected override string GetPropertyDeclaration (PropertyDefinition property)
3984 MethodDefinition gm = null, sm = null;
3986 string get_visible = null;
3987 if ((gm = property.GetMethod) != null &&
3988 (DocUtils.IsExplicitlyImplemented (gm) ||
3989 (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
3990 get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
3991 string set_visible = null;
3992 if ((sm = property.SetMethod) != null &&
3993 (DocUtils.IsExplicitlyImplemented (sm) ||
3994 (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
3995 set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
3997 if ((set_visible == null) && (get_visible == null))
4000 StringBuilder buf = new StringBuilder ()
4001 .Append (".property ");
4002 if (!(gm ?? sm).IsStatic)
4003 buf.Append ("instance ");
4004 _AppendTypeName (buf, property.PropertyType);
4005 buf.Append (' ').Append (property.Name);
4006 if (!property.HasParameters || property.Parameters.Count == 0)
4007 return buf.ToString ();
4011 foreach (ParameterDefinition p in property.Parameters) {
4015 _AppendTypeName (buf, p.ParameterType);
4019 return buf.ToString ();
4022 protected override string GetFieldDeclaration (FieldDefinition field)
4024 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4025 if (declType.IsEnum && field.Name == "value__")
4026 return null; // This member of enums aren't documented.
4028 StringBuilder buf = new StringBuilder ();
4029 AppendFieldVisibility (buf, field);
4030 if (buf.Length == 0)
4033 buf.Insert (0, ".field ");
4036 buf.Append ("static ");
4037 if (field.IsInitOnly)
4038 buf.Append ("initonly ");
4039 if (field.IsLiteral)
4040 buf.Append ("literal ");
4041 _AppendTypeName (buf, field.FieldType);
4042 buf.Append (' ').Append (field.Name);
4043 AppendFieldValue (buf, field);
4045 return buf.ToString ();
4048 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4051 return buf.Append ("public ");
4052 if (field.IsFamilyAndAssembly)
4053 return buf.Append ("familyandassembly ");
4054 if (field.IsFamilyOrAssembly)
4055 return buf.Append ("familyorassembly ");
4057 return buf.Append ("family ");
4061 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4063 // enums have a value__ field, which we ignore
4064 if (field.DeclaringType.IsGenericType ())
4066 if (field.HasConstant && field.IsLiteral) {
4069 val = field.Constant;
4074 buf.Append (" = ").Append ("null");
4075 else if (val is Enum)
4077 .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4079 .Append (val.ToString ())
4081 else if (val is IFormattable) {
4082 string value = ((IFormattable)val).ToString();
4085 buf.Append ("\"" + value + "\"");
4087 buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4096 protected override string GetEventDeclaration (EventDefinition e)
4098 StringBuilder buf = new StringBuilder ();
4099 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4104 buf.Append (".event ")
4105 .Append (GetName (e.EventType))
4109 return buf.ToString ();
4113 class ILMemberFormatter : ILFullMemberFormatter {
4114 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4120 class CSharpFullMemberFormatter : MemberFormatter {
4122 public override string Language {
4126 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4128 string ns = DocUtils.GetNamespace (type);
4129 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
4130 buf.Append (ns).Append ('.');
4134 private string GetCSharpType (string t)
4137 case "System.Byte": return "byte";
4138 case "System.SByte": return "sbyte";
4139 case "System.Int16": return "short";
4140 case "System.Int32": return "int";
4141 case "System.Int64": return "long";
4143 case "System.UInt16": return "ushort";
4144 case "System.UInt32": return "uint";
4145 case "System.UInt64": return "ulong";
4147 case "System.Single": return "float";
4148 case "System.Double": return "double";
4149 case "System.Decimal": return "decimal";
4150 case "System.Boolean": return "bool";
4151 case "System.Char": return "char";
4152 case "System.Void": return "void";
4153 case "System.String": return "string";
4154 case "System.Object": return "object";
4159 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
4161 if (type is GenericParameter)
4162 return AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
4163 string t = type.FullName;
4164 if (!t.StartsWith ("System.")) {
4165 return base.AppendTypeName (buf, type);
4168 string s = GetCSharpType (t);
4170 return buf.Append (s);
4172 return base.AppendTypeName (buf, type);
4175 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
4177 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
4179 GenericParameterAttributes attrs = type.Attributes;
4180 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
4181 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
4185 buf.Append ("out ");
4189 protected override string GetTypeDeclaration (TypeDefinition type)
4191 string visibility = GetTypeVisibility (type.Attributes);
4192 if (visibility == null)
4195 StringBuilder buf = new StringBuilder ();
4197 buf.Append (visibility);
4200 MemberFormatter full = new CSharpFullMemberFormatter ();
4202 if (DocUtils.IsDelegate (type)) {
4203 buf.Append("delegate ");
4204 MethodDefinition invoke = type.GetMethod ("Invoke");
4205 buf.Append (full.GetName (invoke.ReturnType)).Append (" ");
4206 buf.Append (GetName (type));
4207 AppendParameters (buf, invoke, invoke.Parameters);
4208 AppendGenericTypeConstraints (buf, type);
4211 return buf.ToString();
4214 if (type.IsAbstract && !type.IsInterface)
4215 buf.Append("abstract ");
4216 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
4217 buf.Append("sealed ");
4218 buf.Replace ("abstract sealed", "static");
4220 buf.Append (GetTypeKind (type));
4222 buf.Append (GetCSharpType (type.FullName) == null
4227 TypeReference basetype = type.BaseType;
4228 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
4231 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
4232 .Select (iface => full.GetName (iface))
4236 if (basetype != null || interface_names.Count > 0)
4239 if (basetype != null) {
4240 buf.Append (full.GetName (basetype));
4241 if (interface_names.Count > 0)
4245 for (int i = 0; i < interface_names.Count; i++){
4248 buf.Append (interface_names [i]);
4250 AppendGenericTypeConstraints (buf, type);
4253 return buf.ToString ();
4256 static string GetTypeKind (TypeDefinition t)
4262 if (t.IsClass || t.FullName == "System.Enum")
4266 throw new ArgumentException(t.FullName);
4269 static string GetTypeVisibility (TypeAttributes ta)
4271 switch (ta & TypeAttributes.VisibilityMask) {
4272 case TypeAttributes.Public:
4273 case TypeAttributes.NestedPublic:
4276 case TypeAttributes.NestedFamily:
4277 case TypeAttributes.NestedFamORAssem:
4285 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4287 if (type.GenericParameters.Count == 0)
4289 return AppendConstraints (buf, type.GenericParameters);
4292 private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
4294 foreach (GenericParameter genArg in genArgs) {
4295 GenericParameterAttributes attrs = genArg.Attributes;
4296 IList<TypeReference> constraints = genArg.Constraints;
4297 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
4300 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
4301 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
4302 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
4305 if (!isref && !isvt && !isnew && constraints.Count == 0)
4307 buf.Append (" where ").Append (genArg.Name).Append (" : ");
4309 buf.Append ("class");
4313 buf.Append ("struct");
4316 if (constraints.Count > 0 && !isvt) {
4319 buf.Append (GetTypeName (constraints [0]));
4320 for (int i = 1; i < constraints.Count; ++i)
4321 buf.Append (", ").Append (GetTypeName (constraints [i]));
4323 if (isnew && !isvt) {
4326 buf.Append ("new()");
4332 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4334 StringBuilder buf = new StringBuilder ();
4335 AppendVisibility (buf, constructor);
4336 if (buf.Length == 0)
4340 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
4341 AppendParameters (buf, constructor, constructor.Parameters);
4344 return buf.ToString ();
4347 protected override string GetMethodDeclaration (MethodDefinition method)
4349 string decl = base.GetMethodDeclaration (method);
4355 protected override string GetTypeName (MethodReturnType returnType)
4357 return GetTypeName (returnType, () => returnType.ReturnType);
4360 string GetTypeName (ICustomAttributeProvider provider, Func<TypeReference> selector)
4362 string type = GetName (selector ());
4363 if (type == "object" && provider.HasCustomAttributes &&
4364 provider.CustomAttributes.Cast<CustomAttribute>()
4365 .Any (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))
4370 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4372 if (DocUtils.IsExplicitlyImplemented (method)) {
4373 TypeReference iface;
4374 MethodReference ifaceMethod;
4375 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4376 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4378 .Append (ifaceMethod.Name);
4380 return base.AppendMethodName (buf, method);
4383 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4385 if (method.GenericParameters.Count == 0)
4387 return AppendConstraints (buf, method.GenericParameters);
4390 protected override string RefTypeModifier {
4394 protected override string GetFinalizerName (MethodDefinition method)
4396 return "~" + method.DeclaringType.Name + " ()";
4399 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4403 if (method.IsPublic)
4404 return buf.Append ("public");
4405 if (method.IsFamily || method.IsFamilyOrAssembly)
4406 return buf.Append ("protected");
4410 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4412 string modifiers = String.Empty;
4413 if (method.IsStatic) modifiers += " static";
4414 if (method.IsVirtual && !method.IsAbstract) {
4415 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4416 else modifiers += " override";
4418 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4419 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4420 if (method.IsFinal) modifiers += " sealed";
4421 if (modifiers == " virtual sealed") modifiers = "";
4423 return buf.Append (modifiers);
4426 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4428 if (method.IsGenericMethod ()) {
4429 IList<GenericParameter> args = method.GenericParameters;
4430 if (args.Count > 0) {
4432 buf.Append (args [0].Name);
4433 for (int i = 1; i < args.Count; ++i)
4434 buf.Append (",").Append (args [i].Name);
4441 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4443 return AppendParameters (buf, method, parameters, '(', ')');
4446 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4450 if (parameters.Count > 0) {
4451 if (DocUtils.IsExtensionMethod (method))
4452 buf.Append ("this ");
4453 AppendParameter (buf, parameters [0]);
4454 for (int i = 1; i < parameters.Count; ++i) {
4456 AppendParameter (buf, parameters [i]);
4460 return buf.Append (end);
4463 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4465 if (parameter.ParameterType is ByReferenceType) {
4466 if (parameter.IsOut)
4467 buf.Append ("out ");
4469 buf.Append ("ref ");
4471 buf.Append (GetTypeName (parameter, () => parameter.ParameterType)).Append (" ");
4472 return buf.Append (parameter.Name);
4475 protected override string GetPropertyDeclaration (PropertyDefinition property)
4477 MethodDefinition method;
4479 string get_visible = null;
4480 if ((method = property.GetMethod) != null &&
4481 (DocUtils.IsExplicitlyImplemented (method) ||
4482 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
4483 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
4484 string set_visible = null;
4485 if ((method = property.SetMethod) != null &&
4486 (DocUtils.IsExplicitlyImplemented (method) ||
4487 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
4488 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
4490 if ((set_visible == null) && (get_visible == null))
4494 StringBuilder buf = new StringBuilder ();
4495 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
4496 buf.Append (visibility = get_visible);
4497 else if (set_visible != null && get_visible == null)
4498 buf.Append (visibility = set_visible);
4500 buf.Append (visibility = "public");
4502 // Pick an accessor to use for static/virtual/override/etc. checks.
4503 method = property.SetMethod;
4505 method = property.GetMethod;
4507 string modifiers = String.Empty;
4508 if (method.IsStatic) modifiers += " static";
4509 if (method.IsVirtual && !method.IsAbstract) {
4510 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
4511 modifiers += " virtual";
4513 modifiers += " override";
4515 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
4516 if (method.IsAbstract && !declDef.IsInterface)
4517 modifiers += " abstract";
4519 modifiers += " sealed";
4520 if (modifiers == " virtual sealed")
4522 buf.Append (modifiers).Append (' ');
4524 buf.Append (GetName (property.PropertyType)).Append (' ');
4526 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
4527 string name = property.Name;
4528 foreach (MemberReference mi in defs) {
4529 if (mi == property) {
4534 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
4536 if (property.Parameters.Count != 0) {
4537 AppendParameters (buf, method, property.Parameters, '[', ']');
4541 if (get_visible != null) {
4542 if (get_visible != visibility)
4543 buf.Append (' ').Append (get_visible);
4544 buf.Append (" get;");
4546 if (set_visible != null) {
4547 if (set_visible != visibility)
4548 buf.Append (' ').Append (set_visible);
4549 buf.Append (" set;");
4553 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
4556 protected override string GetFieldDeclaration (FieldDefinition field)
4558 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4559 if (declType.IsEnum && field.Name == "value__")
4560 return null; // This member of enums aren't documented.
4562 StringBuilder buf = new StringBuilder ();
4563 AppendFieldVisibility (buf, field);
4564 if (buf.Length == 0)
4567 if (declType.IsEnum)
4570 if (field.IsStatic && !field.IsLiteral)
4571 buf.Append (" static");
4572 if (field.IsInitOnly)
4573 buf.Append (" readonly");
4574 if (field.IsLiteral)
4575 buf.Append (" const");
4577 buf.Append (' ').Append (GetName (field.FieldType)).Append (' ');
4578 buf.Append (field.Name);
4579 AppendFieldValue (buf, field);
4582 return buf.ToString ();
4585 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4588 return buf.Append ("public");
4589 if (field.IsFamily || field.IsFamilyOrAssembly)
4590 return buf.Append ("protected");
4594 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4596 // enums have a value__ field, which we ignore
4597 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
4598 field.DeclaringType.IsGenericType ())
4600 if (field.HasConstant && field.IsLiteral) {
4603 val = field.Constant;
4608 buf.Append (" = ").Append ("null");
4609 else if (val is Enum)
4610 buf.Append (" = ").Append (val.ToString ());
4611 else if (val is IFormattable) {
4612 string value = ((IFormattable)val).ToString();
4614 value = "\"" + value + "\"";
4615 buf.Append (" = ").Append (value);
4621 protected override string GetEventDeclaration (EventDefinition e)
4623 StringBuilder buf = new StringBuilder ();
4624 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4628 AppendModifiers (buf, e.AddMethod);
4630 buf.Append (" event ");
4631 buf.Append (GetName (e.EventType)).Append (' ');
4632 buf.Append (e.Name).Append (';');
4634 return buf.ToString ();
4638 class CSharpMemberFormatter : CSharpFullMemberFormatter {
4639 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4645 class DocTypeFullMemberFormatter : MemberFormatter {
4646 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
4648 protected override char NestedTypeSeparator {
4653 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
4654 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4660 class SlashDocMemberFormatter : MemberFormatter {
4662 protected override char[] GenericTypeContainer {
4663 get {return new char[]{'{', '}'};}
4666 private bool AddTypeCount = true;
4668 private TypeReference genDeclType;
4669 private MethodReference genDeclMethod;
4671 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
4673 if (type is GenericParameter) {
4675 if (genDeclType != null) {
4676 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
4677 for (int i = 0; i < genArgs.Count; ++i) {
4678 if (genArgs [i].Name == type.Name) {
4679 buf.Append ('`').Append (i);
4684 if (genDeclMethod != null) {
4685 IList<GenericParameter> genArgs = null;
4686 if (genDeclMethod.IsGenericMethod ()) {
4687 genArgs = genDeclMethod.GenericParameters;
4688 for (int i = 0; i < genArgs.Count; ++i) {
4689 if (genArgs [i].Name == type.Name) {
4690 buf.Append ("``").Append (i);
4696 if (genDeclType == null && genDeclMethod == null) {
4697 // Probably from within an explicitly implemented interface member,
4698 // where CSC uses parameter names instead of indices (why?), e.g.
4699 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
4700 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
4701 buf.Append (type.Name);
4703 if (buf.Length == l) {
4704 throw new Exception (string.Format (
4705 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
4706 type.Name, genDeclType, genDeclMethod));
4710 base.AppendTypeName (buf, type);
4712 int numArgs = type.GenericParameters.Count;
4713 if (type.DeclaringType != null)
4714 numArgs -= type.GenericParameters.Count;
4716 buf.Append ('`').Append (numArgs);
4723 protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
4725 buf.Append (ArrayDelimeters [0]);
4726 int rank = array.Rank;
4729 for (int i = 1; i < rank; ++i) {
4733 return buf.Append (ArrayDelimeters [1]);
4736 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
4739 base.AppendGenericType (buf, type);
4741 AppendType (buf, type);
4745 private StringBuilder AppendType (StringBuilder buf, TypeReference type)
4747 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
4748 bool insertNested = false;
4749 int prevParamCount = 0;
4750 foreach (var decl in decls) {
4752 buf.Append (NestedTypeSeparator);
4753 insertNested = true;
4754 base.AppendTypeName (buf, decl);
4755 int argCount = DocUtils.GetGenericArgumentCount (decl);
4756 int numArgs = argCount - prevParamCount;
4757 prevParamCount = argCount;
4759 buf.Append ('`').Append (numArgs);
4764 public override string GetDeclaration (MemberReference member)
4766 TypeReference r = member as TypeReference;
4768 return "T:" + GetTypeName (r);
4770 return base.GetDeclaration (member);
4773 protected override string GetConstructorName (MethodReference constructor)
4775 return GetMethodDefinitionName (constructor, "#ctor");
4778 protected override string GetMethodName (MethodReference method)
4781 MethodDefinition methodDef = method as MethodDefinition;
4782 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
4785 TypeReference iface;
4786 MethodReference ifaceMethod;
4787 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
4788 AddTypeCount = false;
4789 name = GetTypeName (iface) + "." + ifaceMethod.Name;
4790 AddTypeCount = true;
4792 return GetMethodDefinitionName (method, name);
4795 private string GetMethodDefinitionName (MethodReference method, string name)
4797 StringBuilder buf = new StringBuilder ();
4798 buf.Append (GetTypeName (method.DeclaringType));
4800 buf.Append (name.Replace (".", "#"));
4801 if (method.IsGenericMethod ()) {
4802 IList<GenericParameter> genArgs = method.GenericParameters;
4803 if (genArgs.Count > 0)
4804 buf.Append ("``").Append (genArgs.Count);
4806 IList<ParameterDefinition> parameters = method.Parameters;
4808 genDeclType = method.DeclaringType;
4809 genDeclMethod = method;
4810 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
4814 genDeclMethod = null;
4816 return buf.ToString ();
4819 private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
4821 if (parameters.Count == 0)
4826 AppendParameter (buf, genArgs, parameters [0]);
4827 for (int i = 1; i < parameters.Count; ++i) {
4829 AppendParameter (buf, genArgs, parameters [i]);
4832 return buf.Append (')');
4835 private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
4837 AddTypeCount = false;
4838 buf.Append (GetTypeName (parameter.ParameterType));
4839 AddTypeCount = true;
4843 protected override string GetPropertyName (PropertyReference property)
4847 PropertyDefinition propertyDef = property as PropertyDefinition;
4848 MethodDefinition method = null;
4849 if (propertyDef != null)
4850 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
4851 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
4852 name = property.Name;
4854 TypeReference iface;
4855 MethodReference ifaceMethod;
4856 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4857 AddTypeCount = false;
4858 name = string.Join ("#", new string[]{
4859 GetTypeName (iface).Replace (".", "#"),
4860 DocUtils.GetMember (property.Name)
4862 AddTypeCount = true;
4865 StringBuilder buf = new StringBuilder ();
4866 buf.Append (GetName (property.DeclaringType));
4869 IList<ParameterDefinition> parameters = property.Parameters;
4870 if (parameters.Count > 0) {
4871 genDeclType = property.DeclaringType;
4873 IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
4874 AppendParameter (buf, genArgs, parameters [0]);
4875 for (int i = 1; i < parameters.Count; ++i) {
4877 AppendParameter (buf, genArgs, parameters [i]);
4882 return buf.ToString ();
4885 protected override string GetFieldName (FieldReference field)
4887 return string.Format ("{0}.{1}",
4888 GetName (field.DeclaringType), field.Name);
4891 protected override string GetEventName (EventReference e)
4893 return string.Format ("{0}.{1}",
4894 GetName (e.DeclaringType), e.Name);
4897 protected override string GetTypeDeclaration (TypeDefinition type)
4899 string name = GetName (type);
4905 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4907 string name = GetName (constructor);
4913 protected override string GetMethodDeclaration (MethodDefinition method)
4915 string name = GetName (method);
4918 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4919 genDeclType = method.DeclaringType;
4920 genDeclMethod = method;
4921 name += "~" + GetName (method.ReturnType);
4923 genDeclMethod = null;
4928 protected override string GetPropertyDeclaration (PropertyDefinition property)
4930 string name = GetName (property);
4936 protected override string GetFieldDeclaration (FieldDefinition field)
4938 string name = GetName (field);
4944 protected override string GetEventDeclaration (EventDefinition e)
4946 string name = GetName (e);
4953 class FileNameMemberFormatter : SlashDocMemberFormatter {
4954 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4959 protected override char NestedTypeSeparator {