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.Globalization;
13 using System.Xml.XPath;
18 using MyXmlNodeList = System.Collections.Generic.List<System.Xml.XmlNode>;
19 using StringList = System.Collections.Generic.List<string>;
20 using StringToStringMap = System.Collections.Generic.Dictionary<string, string>;
21 using StringToXmlNodeMap = System.Collections.Generic.Dictionary<string, System.Xml.XmlNode>;
23 namespace Mono.Documentation {
25 class MDocUpdaterOptions
27 public string path = null;
28 public string updateto = null;
29 public List<string> assembly = null;
30 public List<string> type = null;
31 public string @namespace = null;
32 public bool delete = false;
33 public bool overrides = true;
34 public bool ignoremembers = false;
35 public bool ignore_extra_docs = false;
38 public bool pretty = true;
40 public bool show_exceptions;
41 public ExceptionLocations? exceptions;
44 class MDocUpdater : MDocCommand
47 static string srcPath;
48 static List<AssemblyDefinition> assemblies;
50 static bool nooverrides = true, delete = false, ignoremembers = false;
51 static bool pretty = false;
52 static bool show_exceptions = false;
53 static ExceptionLocations? exceptions;
55 static int additions = 0, deletions = 0;
58 static XmlDocument slashdocs;
59 static XmlReader ecmadocs;
63 static MemberFormatter csharpFullFormatter = new CSharpFullMemberFormatter ();
64 static MemberFormatter csharpFormatter = new CSharpMemberFormatter ();
65 static MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
66 static MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
67 static MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
69 static MyXmlNodeList extensionMethods = new MyXmlNodeList ();
71 public override void Run (IEnumerable<string> args)
73 var opts = new MDocUpdaterOptions {
76 show_exceptions = DebugOutput,
79 opts.type = new List<string> ();
80 var p = new OptionSet () {
82 "Delete removed members from the XML files.",
83 v => opts.delete = v != null },
85 "Document potential exceptions that members can generate. {SOURCES} " +
86 "is a comma-separated list of:\n" +
87 " asm Method calls in same assembly\n" +
88 " depasm Method calls in dependent assemblies\n" +
89 " all Record all possible exceptions\n" +
90 "If nothing is specified, then only exceptions from the member will " +
92 v => opts.exceptions = ParseExceptionLocations (v) },
94 "Import documentation from {FILE}.",
95 v => opts.import = v },
97 "Root {DIRECTORY} to generate/update documentation.",
100 "Manually specify the assembly {VERSION} that new members were added in.",
101 v => opts.since = v },
103 "Only update documentation for {TYPE}.",
104 v => opts.type.Add (v) },
106 opts.assembly = Parse (p, args, "update",
107 "[OPTIONS]+ ASSEMBLIES",
108 "Create or update documentation from ASSEMBLIES.");
109 if (opts.assembly == null)
111 if (opts.assembly.Count == 0)
112 base.Error ("No assemblies specified.");
115 opts.name = ""; // remove warning about unused member
118 static ExceptionLocations ParseExceptionLocations (string s)
120 ExceptionLocations loc = ExceptionLocations.Member;
123 foreach (var type in s.Split (',')) {
125 case "asm": loc |= ExceptionLocations.Assembly; break;
126 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
127 case "all": loc = ExceptionLocations.All; break;
128 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
134 static void Run (MDocUpdaterOptions opts)
136 nooverrides = !opts.overrides;
137 delete = opts.delete;
138 ignoremembers = opts.ignoremembers;
140 pretty = opts.pretty;
142 show_exceptions = opts.show_exceptions;
143 exceptions = opts.exceptions;
145 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
147 if (opts.path == null)
148 throw new InvalidOperationException("The --out option is required.");
152 if (opts.type != null && opts.type.Count > 0 && opts.@namespace != null)
153 throw new InvalidOperationException("You cannot specify both 'type' and 'namespace'.");
155 if (opts.assembly == null)
156 throw new InvalidOperationException("The assembly option is required.");
158 assemblies = opts.assembly.Select (a => LoadAssembly (a)).ToList ();
160 if (opts.import != null && ecmadocs == null && slashdocs == null) {
162 XmlReader r = new XmlTextReader (opts.import);
164 while (r.NodeType != XmlNodeType.Element) {
166 throw new Exception ("Unable to read XML file: " +
169 if (r.LocalName == "doc") {
170 slashdocs = new XmlDocument();
171 slashdocs.Load (opts.import);
173 else if (r.LocalName == "Libraries") {
174 ecmadocs = new XmlTextReader (opts.import);
177 throw new Exception ("Unsupported XML format within " + opts.import);
180 } catch (Exception e) {
181 Error ("Could not load XML file: {0}", e.Message);
182 Environment.ExitCode = 1;
187 // PERFORM THE UPDATES
189 string dest_dir = opts.updateto != null ? opts.updateto : opts.path;
190 if (opts.type != null && opts.type.Count > 0)
191 DoUpdateTypes(opts.path, opts.type, dest_dir);
192 else if (opts.@namespace != null)
193 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
194 Path.Combine (dest_dir, opts.@namespace));
196 DoUpdateAssemblies(opts.path, dest_dir);
198 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
201 private static new void Error (string format, params object[] args)
203 Console.Error.Write ("mdoc: ");
204 Console.Error.WriteLine (format, args);
207 private static AssemblyDefinition LoadAssembly (string name)
209 AssemblyDefinition assembly = null;
211 assembly = AssemblyFactory.GetAssembly (name);
212 } catch (System.IO.FileNotFoundException) { }
214 if (assembly == null)
215 throw new InvalidOperationException("Assembly " + name + " not found.");
217 var r = assembly.Resolver as BaseAssemblyResolver;
218 if (r != null && name.Contains (Path.DirectorySeparatorChar)) {
219 r.AddSearchDirectory (Path.GetDirectoryName (name));
224 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
225 OrderTypeAttributes (element);
226 XmlTextWriter writer = new XmlTextWriter(output);
227 writer.Formatting = Formatting.Indented;
228 writer.Indentation = 2;
229 writer.IndentChar = ' ';
230 element.WriteTo(writer);
234 private static void OrderTypeAttributes (XmlElement e)
236 foreach (XmlElement type in e.SelectNodes ("//Type")) {
237 OrderTypeAttributes (type.Attributes);
241 static readonly string[] TypeAttributeOrder = {
242 "Name", "FullName", "FullNameSP", "Maintainer"
245 private static void OrderTypeAttributes (XmlAttributeCollection c)
247 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
248 for (int i = 0; i < c.Count; ++i) {
249 XmlAttribute a = c [i];
250 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
251 if (a.Name == TypeAttributeOrder [j]) {
257 for (int i = attrs.Length-1; i >= 0; --i) {
258 XmlAttribute n = attrs [i];
261 XmlAttribute r = null;
262 for (int j = i+1; j < attrs.Length; ++j) {
263 if (attrs [j] != null) {
271 c.InsertBefore (n, r);
275 private static XmlDocument CreateIndexStub() {
276 XmlDocument index = new XmlDocument();
278 XmlElement index_root = index.CreateElement("Overview");
279 index.AppendChild(index_root);
281 if (assemblies.Count == 0)
282 throw new Exception ("No assembly");
284 XmlElement index_assemblies = index.CreateElement("Assemblies");
285 index_root.AppendChild(index_assemblies);
287 XmlElement index_remarks = index.CreateElement("Remarks");
288 index_remarks.InnerText = "To be added.";
289 index_root.AppendChild(index_remarks);
291 XmlElement index_copyright = index.CreateElement("Copyright");
292 index_copyright.InnerText = "To be added.";
293 index_root.AppendChild(index_copyright);
295 XmlElement index_types = index.CreateElement("Types");
296 index_root.AppendChild(index_types);
301 private static void WriteNamespaceStub(string ns, string outdir) {
302 XmlDocument index = new XmlDocument();
304 XmlElement index_root = index.CreateElement("Namespace");
305 index.AppendChild(index_root);
307 index_root.SetAttribute("Name", ns);
309 XmlElement index_docs = index.CreateElement("Docs");
310 index_root.AppendChild(index_docs);
312 XmlElement index_summary = index.CreateElement("summary");
313 index_summary.InnerText = "To be added.";
314 index_docs.AppendChild(index_summary);
316 XmlElement index_remarks = index.CreateElement("remarks");
317 index_remarks.InnerText = "To be added.";
318 index_docs.AppendChild(index_remarks);
320 using (TextWriter writer = OpenWrite (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew)) {
321 WriteXml(index.DocumentElement, writer);
325 public static void DoUpdateTypes (string basepath, List<string> typenames, string dest)
327 var found = new HashSet<string> ();
328 foreach (AssemblyDefinition assembly in assemblies) {
329 foreach (DocsTypeInfo docsTypeInfo in GetTypes (assembly, typenames)) {
330 string relpath = DoUpdateType (docsTypeInfo.Type, basepath, dest, docsTypeInfo.EcmaDocs);
332 found.Add (docsTypeInfo.Type.FullName);
335 var notFound = from n in typenames where !found.Contains (n) select n;
337 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
340 public static string DoUpdateType(TypeDefinition type, string basepath, string dest, XmlReader ecmaDocsType)
342 if (type.Namespace == null)
343 Error ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
345 if (!IsPublic (type))
348 // Must get the A+B form of the type name.
349 string typename = GetTypeFileName(type);
351 string reltypefile = DocUtils.PathCombine (DocUtils.GetNamespace (type), typename + ".xml");
352 string typefile = Path.Combine (basepath, reltypefile);
353 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
355 string output = null;
358 } else if (dest == "-") {
361 output = Path.Combine (dest, reltypefile);
366 XmlDocument basefile = new XmlDocument();
367 if (!pretty) basefile.PreserveWhitespace = true;
369 basefile.Load(typefile);
370 } catch (Exception e) {
371 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
374 DoUpdateType2("Updating", basefile, type, output, false, ecmaDocsType);
377 XmlElement td = StubType(type, output, ecmaDocsType);
381 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
384 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
390 public static void DoUpdateNS(string ns, string nspath, string outpath) {
391 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
392 AssemblyDefinition assembly = assemblies [0];
394 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
395 XmlDocument basefile = new XmlDocument();
396 if (!pretty) basefile.PreserveWhitespace = true;
397 string typefile = Path.Combine(nspath, file.Name);
399 basefile.Load(typefile);
400 } catch (Exception e) {
401 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
405 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
406 TypeDefinition type = assembly.GetType(typename);
408 Error ("Type no longer in assembly: " + typename);
412 seenTypes[type] = seenTypes;
413 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false, null);
416 // Stub types not in the directory
417 foreach (DocsTypeInfo docsTypeInfo in GetTypes (assembly, null)) {
418 TypeDefinition type = docsTypeInfo.Type;
419 if (type.Namespace != ns || seenTypes.ContainsKey(type))
422 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"), docsTypeInfo.EcmaDocs);
423 if (td == null) continue;
427 private static string GetTypeFileName (TypeReference type)
429 return filenameFormatter.GetName (type);
432 public static string GetTypeFileName (string typename)
434 StringBuilder filename = new StringBuilder (typename.Length);
438 for (int i = 0; i < typename.Length; ++i) {
439 char c = typename [i];
448 filename.Append ('`').Append ((numArgs+1).ToString());
463 return filename.ToString ();
466 private static void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
468 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
469 index_assembly.SetAttribute ("Name", assembly.Name.Name);
470 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
471 MakeAttributes (index_assembly, assembly.CustomAttributes, true);
472 parent.AppendChild(index_assembly);
475 private static void DoUpdateAssemblies (string source, string dest)
477 string indexfile = dest + "/index.xml";
479 if (System.IO.File.Exists(indexfile)) {
480 index = new XmlDocument();
481 index.Load(indexfile);
484 ClearElement(index.DocumentElement, "Assembly");
485 ClearElement(index.DocumentElement, "Attributes");
487 index = CreateIndexStub();
491 string defaultTitle = "Untitled";
492 if (assemblies.Count == 1)
493 defaultTitle = assemblies[0].Name.Name;
494 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
496 WriteElementText(index.DocumentElement, "Title", name);
499 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
500 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
501 index_assemblies.RemoveAll ();
504 HashSet<string> goodfiles = new HashSet<string> ();
506 foreach (AssemblyDefinition assm in assemblies) {
507 AddIndexAssembly (assm, index_assemblies);
508 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
511 SortIndexEntries (index_types);
513 CleanupFiles (dest, goodfiles);
514 CleanupIndexTypes (index_types, goodfiles);
515 CleanupExtensions (index_types);
517 using (TextWriter writer = OpenWrite (indexfile, FileMode.Create))
518 WriteXml(index.DocumentElement, writer);
521 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
523 private static void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
525 foreach (DocsTypeInfo docTypeInfo in GetTypes (assembly, null)) {
526 TypeDefinition type = docTypeInfo.Type;
527 string typename = GetTypeFileName(type);
528 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0)
531 string reltypepath = DoUpdateType (type, source, dest, docTypeInfo.EcmaDocs);
532 if (reltypepath == null)
535 // Add namespace and type nodes into the index file as needed
536 string ns = DocUtils.GetNamespace (type);
537 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode("Namespace[@Name='" + ns + "']");
538 if (nsnode == null) {
539 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
540 nsnode.SetAttribute ("Name", ns);
541 index_types.AppendChild(nsnode);
543 string doc_typename = GetDocTypeName (type);
544 XmlElement typenode = (XmlElement)nsnode.SelectSingleNode("Type[@Name='" + typename + "']");
545 if (typenode == null) {
546 typenode = index_types.OwnerDocument.CreateElement("Type");
547 typenode.SetAttribute("Name", typename);
548 nsnode.AppendChild(typenode);
550 if (typename != doc_typename)
551 typenode.SetAttribute("DisplayName", doc_typename);
553 typenode.RemoveAttribute("DisplayName");
554 typenode.SetAttribute ("Kind", GetTypeKind (type));
556 // Ensure the namespace index file exists
557 string onsdoc = DocUtils.PathCombine (dest, type.Namespace + ".xml");
558 string nsdoc = DocUtils.PathCombine (dest, "ns-" + type.Namespace + ".xml");
559 if (File.Exists (onsdoc)) {
560 File.Move (onsdoc, nsdoc);
563 if (!File.Exists (nsdoc)) {
564 Console.WriteLine("New Namespace File: " + type.Namespace);
565 WriteNamespaceStub(type.Namespace, dest);
568 goodfiles.Add (reltypepath);
573 public TypeDefinition Type;
574 public XmlReader EcmaDocs;
576 public DocsTypeInfo (TypeDefinition type, XmlReader docs)
579 this.EcmaDocs = docs;
583 static IEnumerable<Mono.Documentation.MDocUpdater.DocsTypeInfo> GetTypes (AssemblyDefinition assembly, List<string> forTypes)
585 HashSet<string> seen = null;
586 if (forTypes != null)
588 if (ecmadocs != null) {
589 seen = new HashSet<string> ();
591 while (ecmadocs.Read ()) {
592 switch (ecmadocs.Name) {
595 typeDepth = ecmadocs.Depth;
596 if (ecmadocs.NodeType != XmlNodeType.Element)
598 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
600 string typename = ecmadocs.GetAttribute ("FullName");
601 string typename2 = GetTypeFileName (typename);
602 if (forTypes != null &&
603 forTypes.BinarySearch (typename) < 0 &&
604 typename != typename2 &&
605 forTypes.BinarySearch (typename2) < 0)
608 if ((t = assembly.GetType (typename)) == null &&
609 (t = assembly.GetType (typename2)) == null)
612 if (typename != typename2)
613 seen.Add (typename2);
614 Console.WriteLine (" Import: {0}", t.FullName);
615 yield return new DocsTypeInfo (t, ecmadocs);
623 foreach (TypeDefinition type in assembly.GetTypes()) {
624 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
626 if (seen != null && seen.Contains (type.FullName))
628 yield return new DocsTypeInfo (type, null);
629 foreach (TypeDefinition nested in type.NestedTypes)
630 yield return new DocsTypeInfo (nested, null);
634 private static void SortIndexEntries (XmlElement indexTypes)
636 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
637 XmlNodeComparer c = new AttributeNameComparer ();
638 SortXmlNodes (indexTypes, namespaces, c);
640 for (int i = 0; i < namespaces.Count; ++i)
641 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
644 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
646 MyXmlNodeList l = new MyXmlNodeList (children.Count);
647 for (int i = 0; i < children.Count; ++i)
648 l.Add (children [i]);
650 for (int i = l.Count - 1; i > 0; --i) {
651 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
655 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
657 public abstract int Compare (XmlNode x, XmlNode y);
659 public int Compare (object x, object y)
661 return Compare ((XmlNode) x, (XmlNode) y);
665 class AttributeNameComparer : XmlNodeComparer {
668 public AttributeNameComparer ()
673 public AttributeNameComparer (string attribute)
675 this.attribute = attribute;
678 public override int Compare (XmlNode x, XmlNode y)
680 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
684 class VersionComparer : XmlNodeComparer {
685 public override int Compare (XmlNode x, XmlNode y)
687 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
688 string a = GetVersion (x.InnerText);
689 string b = GetVersion (y.InnerText);
690 return new Version (a).CompareTo (new Version (b));
693 static string GetVersion (string v)
695 int n = v.IndexOf ("x");
698 return v.Substring (0, n-1);
702 private static string GetTypeKind (TypeDefinition type)
705 return "Enumeration";
706 if (type.IsValueType)
708 if (type.IsInterface)
710 if (DocUtils.IsDelegate (type))
712 if (type.IsClass || type.FullName == "System.Enum") // FIXME
714 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
717 private static bool IsPublic (TypeDefinition type)
719 TypeDefinition decl = type;
720 while (decl != null) {
721 if (!(decl.IsPublic || decl.IsNestedPublic)) {
724 decl = (TypeDefinition) decl.DeclaringType;
729 private static void CleanupFiles (string dest, HashSet<string> goodfiles)
731 // Look for files that no longer correspond to types
732 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
733 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
734 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
735 if (!goodfiles.Contains (relTypeFile)) {
736 XmlDocument doc = new XmlDocument ();
737 doc.Load (typefile.FullName);
738 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
739 if (UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
740 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
741 WriteXml(doc.DocumentElement, writer);
742 goodfiles.Add (relTypeFile);
745 string newname = typefile.FullName + ".remove";
746 try { System.IO.File.Delete(newname); } catch (Exception) { }
747 try { typefile.MoveTo(newname); } catch (Exception) { }
748 Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
754 private static TextWriter OpenWrite (string path, FileMode mode)
756 return new StreamWriter (
757 new FileStream (path, mode),
758 new UTF8Encoding (false)
762 private static string[] GetAssemblyVersions ()
764 return (from a in assemblies select GetAssemblyVersion (a)).ToArray ();
767 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
769 // Look for type nodes that no longer correspond to types
770 MyXmlNodeList remove = new MyXmlNodeList ();
771 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
772 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
773 if (!goodfiles.Contains (fulltypename)) {
774 remove.Add (typenode);
777 foreach (XmlNode n in remove)
778 n.ParentNode.RemoveChild (n);
781 private static void CleanupExtensions (XmlElement index_types)
783 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
784 if (extensionMethods.Count == 0) {
787 index_types.RemoveChild (e);
791 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
792 index_types.SelectSingleNode ("/Overview").AppendChild (e);
796 extensionMethods.Sort (DefaultExtensionMethodComparer);
797 foreach (XmlNode m in extensionMethods) {
798 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
802 class ExtensionMethodComparer : XmlNodeComparer {
803 public override int Compare (XmlNode x, XmlNode y)
805 XmlNode xLink = x.SelectSingleNode ("Member/Link");
806 XmlNode yLink = y.SelectSingleNode ("Member/Link");
808 int n = xLink.Attributes ["Type"].Value.CompareTo (
809 yLink.Attributes ["Type"].Value);
812 n = xLink.Attributes ["Member"].Value.CompareTo (
813 yLink.Attributes ["Member"].Value);
814 if (n == 0 && !object.ReferenceEquals (x, y))
815 throw new InvalidOperationException ("Duplicate extension method found!");
820 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
822 public static void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince, XmlReader ecmaDocsType)
824 Console.WriteLine(message + ": " + type.FullName);
826 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
828 // Update type metadata
829 UpdateType(basefile.DocumentElement, type, ecmaDocsType);
831 if (ecmaDocsType != null) {
832 while (ecmaDocsType.Name != "Members" && ecmaDocsType.Read ()) {
835 if (ecmaDocsType.IsEmptyElement)
839 // Update existing members. Delete member nodes that no longer should be there,
840 // and remember what members are already documented so we don't add them again.
841 if (!ignoremembers) {
842 MyXmlNodeList todelete = new MyXmlNodeList ();
843 foreach (DocsNodeInfo info in GetDocumentationMembers (basefile, type, ecmaDocsType)) {
844 XmlElement oldmember = info.Node;
845 IMemberReference oldmember2 = info.Member;
846 string sig = oldmember2 != null ? MakeMemberSignature(oldmember2) : null;
848 // Interface implementations and overrides are deleted from the docs
849 // unless the overrides option is given.
850 if (oldmember2 != null && (!IsNew(oldmember2) || sig == null))
853 // Deleted (or signature changed)
854 if (oldmember2 == null) {
855 if (UpdateAssemblyVersions (oldmember, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
857 DeleteMember ("Member Removed", output, oldmember, todelete);
862 if (seenmembers.ContainsKey (sig)) {
863 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
864 // ignore, already seen
866 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
867 DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
869 Error ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
873 // Update signature information
876 seenmembers.Add (sig, oldmember);
878 foreach (XmlElement oldmember in todelete)
879 oldmember.ParentNode.RemoveChild (oldmember);
882 if (!DocUtils.IsDelegate (type) && !ignoremembers) {
883 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
884 foreach (IMemberReference m in type.GetMembers()) {
885 if (m is TypeDefinition) continue;
887 string sig = MakeMemberSignature(m);
888 if (sig == null) continue;
889 if (seenmembers.ContainsKey(sig)) continue;
891 // To be nice on diffs, members/properties/events that are overrides or are interface implementations
893 if (!IsNew(m)) continue;
895 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
896 if (mm == null) continue;
897 members.AppendChild( mm );
899 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
904 // Import code snippets from files
905 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
906 if (!(code is XmlElement)) continue;
907 string file = ((XmlElement)code).GetAttribute("src");
908 string lang = ((XmlElement)code).GetAttribute("lang");
910 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
912 code.InnerText = src;
916 if (insertSince && since != null) {
917 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
918 docs.AppendChild (CreateSinceNode (basefile));
922 XmlElement d = basefile.DocumentElement ["Docs"];
923 XmlElement m = basefile.DocumentElement ["Members"];
924 if (d != null && m != null)
925 basefile.DocumentElement.InsertBefore (
926 basefile.DocumentElement.RemoveChild (d), m);
930 System.IO.TextWriter writer;
932 writer = Console.Out;
934 FileInfo file = new FileInfo (output);
935 if (!file.Directory.Exists) {
936 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
937 file.Directory.Create ();
939 writer = OpenWrite (output, FileMode.Create);
943 WriteXml(basefile.DocumentElement, writer);
946 private static string GetCodeSource (string lang, string file)
949 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
950 // Grab the specified region
951 string region = "#region " + file.Substring (anchorStart + 4);
952 file = file.Substring (0, anchorStart + 3);
954 using (StreamReader reader = new StreamReader (file)) {
956 StringBuilder src = new StringBuilder ();
958 while ((line = reader.ReadLine ()) != null) {
959 if (line.Trim() == region) {
960 indent = line.IndexOf (region);
963 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
968 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
971 return src.ToString ();
973 } catch (Exception e) {
974 Error ("Could not load <code/> file '{0}' region '{1}': {2}",
975 file, region, show_exceptions ? e.ToString () : e.Message);
980 using (StreamReader reader = new StreamReader (file))
981 return reader.ReadToEnd ();
982 } catch (Exception e) {
983 Error ("Could not load <code/> file '" + file + "': " + e.Message);
988 private static IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type, XmlReader ecmaDocsMembers)
990 if (ecmaDocsMembers != null) {
991 int membersDepth = ecmaDocsMembers.Depth;
993 while (go && ecmaDocsMembers.Read ()) {
994 switch (ecmaDocsMembers.Name) {
996 if (membersDepth != ecmaDocsMembers.Depth - 1 || ecmaDocsMembers.NodeType != XmlNodeType.Element)
998 DocumentationMember dm = new DocumentationMember (ecmaDocsMembers);
999 string xp = GetXPathForMember (dm);
1000 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
1002 if (oldmember == null) {
1003 m = GetMember (type, dm);
1005 Error ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
1006 type.FullName, dm.MemberSignatures ["C#"]);
1007 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
1010 // oldmember lookup may have failed due to type parameter renames.
1012 oldmember = (XmlElement) basefile.SelectSingleNode (GetXPathForMember (m));
1013 if (oldmember == null) {
1014 XmlElement members = WriteElement(basefile.DocumentElement, "Members");
1015 oldmember = basefile.CreateElement ("Member");
1016 oldmember.SetAttribute ("MemberName", dm.MemberName);
1017 members.AppendChild (oldmember);
1018 foreach (string key in Sort (dm.MemberSignatures.Keys)) {
1019 XmlElement ms = basefile.CreateElement ("MemberSignature");
1020 ms.SetAttribute ("Language", key);
1021 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
1022 oldmember.AppendChild (ms);
1024 oldmember.SetAttribute ("__monodocer-seen__", "true");
1025 Console.WriteLine ("Member Added: {0}", MakeMemberSignature (m));
1030 m = GetMember (type, new DocumentationMember (oldmember));
1032 Error ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
1033 type.FullName, dm.MemberSignatures ["C#"]);
1036 oldmember.SetAttribute ("__monodocer-seen__", "true");
1038 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
1039 if (ecmaDocsMembers.Name != "Docs")
1040 throw new InvalidOperationException ("Found " + ecmaDocsMembers.Name + "; expected <Docs/>!");
1041 node.EcmaDocs = ecmaDocsMembers;
1046 if (membersDepth == ecmaDocsMembers.Depth && ecmaDocsMembers.NodeType == XmlNodeType.EndElement) {
1053 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
1054 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
1055 oldmember.RemoveAttribute ("__monodocer-seen__");
1058 IMemberReference m = GetMember (type, new DocumentationMember (oldmember));
1060 yield return new DocsNodeInfo (oldmember);
1063 yield return new DocsNodeInfo (oldmember, m);
1068 static void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
1070 string format = output != null
1071 ? "{0}: File='{1}'; Signature='{4}'"
1072 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1076 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1077 member.Attributes ["MemberName"].Value,
1078 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
1079 if (!delete && MemberDocsHaveUserContent (member)) {
1080 Error ("Member deletions must be enabled with the --delete option.");
1082 todelete.Add (member);
1087 class MemberComparer : XmlNodeComparer {
1088 public override int Compare (XmlNode x, XmlNode y)
1091 string xMemberName = x.Attributes ["MemberName"].Value;
1092 string yMemberName = y.Attributes ["MemberName"].Value;
1094 // generic methods *end* with '>'
1095 // it's possible for explicitly implemented generic interfaces to
1096 // contain <...> without being a generic method
1097 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1098 (r = xMemberName.CompareTo (yMemberName)) != 0)
1102 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1103 xMemberName = xMemberName.Substring (0, lt);
1104 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1105 yMemberName = yMemberName.Substring (0, lt);
1106 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1109 // if @MemberName matches, then it's either two different types of
1110 // members sharing the same name, e.g. field & property, or it's an
1111 // overloaded method.
1112 // for different type, sort based on MemberType value.
1113 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1114 y.SelectSingleNode ("MemberType").InnerText);
1118 // same type -- must be an overloaded method. Sort based on type
1119 // parameter count, then parameter count, then by the parameter
1121 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1122 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1123 if (xTypeParams.Count != yTypeParams.Count)
1124 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1125 for (int i = 0; i < xTypeParams.Count; ++i) {
1126 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1127 yTypeParams [i].Attributes ["Name"].Value);
1132 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1133 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1134 if (xParams.Count != yParams.Count)
1135 return xParams.Count <= yParams.Count ? -1 : 1;
1136 for (int i = 0; i < xParams.Count; ++i) {
1137 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1138 yParams [i].Attributes ["Type"].Value);
1142 // all parameters match, but return value might not match if it was
1143 // changed between one version and another.
1144 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1145 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1146 if (xReturn != null && yReturn != null) {
1147 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1156 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1158 private static void SortTypeMembers (XmlNode members)
1160 if (members == null)
1162 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1165 private static bool MemberDocsHaveUserContent (XmlNode e)
1167 e = (XmlElement)e.SelectSingleNode("Docs");
1168 if (e == null) return false;
1169 foreach (XmlElement d in e.SelectNodes("*"))
1170 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1175 private static bool IsNew (IMemberReference m)
1177 if (!nooverrides) return true;
1178 if (m is MethodDefinition && !IsNew ((MethodDefinition)m)) return false;
1179 if (m is PropertyDefinition && !IsNew (((PropertyDefinition)m).GetMethod)) return false;
1180 if (m is PropertyDefinition && !IsNew (((PropertyDefinition)m).SetMethod)) return false;
1181 if (m is EventDefinition && !IsNew (((EventDefinition)m).AddMethod)) return false;
1182 if (m is EventDefinition && !IsNew (((EventDefinition)m).InvokeMethod)) return false;
1183 if (m is EventDefinition && !IsNew (((EventDefinition)m).RemoveMethod)) return false;
1187 private static bool IsNew (MethodDefinition m)
1194 // UPDATE HELPER FUNCTIONS
1196 private static IMemberReference GetMember (TypeDefinition type, DocumentationMember member)
1198 string membertype = member.MemberType;
1200 string returntype = member.ReturnType;
1202 string docName = member.MemberName;
1203 string[] docTypeParams = GetTypeParameters (docName);
1205 // Loop through all members in this type with the same name
1206 foreach (IMemberReference mi in GetReflectionMembers (type, docName)) {
1207 if (mi is TypeDefinition) continue;
1208 if (GetMemberType(mi) != membertype) continue;
1210 string sig = MakeMemberSignature(mi);
1211 if (sig == null) continue; // not publicly visible
1213 ParameterDefinitionCollection pis = null;
1214 string[] typeParams = null;
1215 if (mi is MethodDefinition) {
1216 MethodDefinition mb = (MethodDefinition) mi;
1217 pis = mb.Parameters;
1218 if (docTypeParams != null && mb.IsGenericMethod ()) {
1219 GenericParameterCollection args = mb.GenericParameters;
1220 if (args.Count == docTypeParams.Length) {
1221 typeParams = args.Cast<GenericParameter> ().Select (p => p.Name).ToArray ();
1225 else if (mi is PropertyDefinition)
1226 pis = ((PropertyDefinition)mi).Parameters;
1228 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
1229 int pcount = pis == null ? 0 : pis.Count;
1230 if (mcount != pcount)
1233 MethodDefinition mDef = mi as MethodDefinition;
1234 if (mDef != null && !mDef.IsConstructor) {
1235 // Casting operators can overload based on return type.
1236 if (returntype != GetReplacedString (
1237 GetDocTypeFullName (((MethodDefinition)mi).ReturnType.ReturnType),
1238 typeParams, docTypeParams)) {
1246 for (int i = 0; i < pis.Count; i++) {
1247 string paramType = GetReplacedString (
1248 GetDocParameterType (pis [i].ParameterType),
1249 typeParams, docTypeParams);
1250 if (paramType != (string) member.Parameters [i]) {
1255 if (!good) continue;
1263 private static IEnumerable<IMemberReference> GetReflectionMembers (TypeDefinition type, string docName)
1265 // need to worry about 4 forms of //@MemberName values:
1266 // 1. "Normal" (non-generic) member names: GetEnumerator
1268 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
1269 // - try as-is, and try type.member (due to "kludge" for property
1271 // 3. "Normal" Generic member names: Sort<T> (CSC)
1272 // - need to remove generic parameters --> "Sort"
1273 // 4. Explicitly-implemented interface members for generic interfaces:
1274 // -- System.Collections.Generic.IEnumerable<T>.Current
1275 // - Try as-is, and try type.member, *keeping* the generic parameters.
1276 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
1277 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
1278 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
1279 // this as (1) or (2).
1280 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
1282 foreach (IMemberReference mi in type.GetMembers (docName))
1284 if (CountChars (docName, '.') > 0)
1285 // might be a property; try only type.member instead of
1286 // namespace.type.member.
1287 foreach (IMemberReference mi in
1288 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
1295 int startLt, startType, startMethod;
1296 startLt = startType = startMethod = -1;
1297 for (int i = 0; i < docName.Length; ++i) {
1298 switch (docName [i]) {
1307 if (numLt == 0 && (i + 1) < docName.Length)
1308 // there's another character in docName, so this <...> sequence is
1309 // probably part of a generic type -- case 4.
1313 startType = startMethod;
1319 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
1321 foreach (IMemberReference mi in type.GetMembers (refName))
1325 foreach (IMemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
1328 // If we _still_ haven't found it, we've hit another generic naming issue:
1329 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
1330 // explicitly-implemented METHOD names (not properties), e.g.
1331 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
1332 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
1333 // which the XML docs will contain.
1335 // Alas, we can't derive the Mono name from docName, so we need to iterate
1336 // over all member names, convert them into CSC format, and compare... :-(
1339 foreach (IMemberReference mi in type.GetMembers ()) {
1340 if (GetMemberName (mi) == docName)
1345 static string[] GetTypeParameters (string docName)
1347 if (docName [docName.Length-1] != '>')
1349 StringList types = new StringList ();
1350 int endToken = docName.Length-2;
1351 int i = docName.Length-2;
1353 if (docName [i] == ',' || docName [i] == '<') {
1354 types.Add (docName.Substring (i + 1, endToken - i));
1357 if (docName [i] == '<')
1362 return types.ToArray ();
1365 static string GetReplacedString (string typeName, string[] from, string[] to)
1369 for (int i = 0; i < from.Length; ++i)
1370 typeName = typeName.Replace (from [i], to [i]);
1374 // CREATE A STUB DOCUMENTATION FILE
1376 public static XmlElement StubType (TypeDefinition type, string output, XmlReader ecmaDocsType)
1378 string typesig = MakeTypeSignature(type);
1379 if (typesig == null) return null; // not publicly visible
1381 XmlDocument doc = new XmlDocument();
1382 XmlElement root = doc.CreateElement("Type");
1383 doc.AppendChild (root);
1385 DoUpdateType2 ("New Type", doc, type, output, true, ecmaDocsType);
1390 private static XmlElement CreateSinceNode (XmlDocument doc)
1392 XmlElement s = doc.CreateElement ("since");
1393 s.SetAttribute ("version", since);
1397 // STUBBING/UPDATING FUNCTIONS
1399 public static void UpdateType (XmlElement root, TypeDefinition type, XmlReader ecmaDocsType)
1401 root.SetAttribute("Name", GetDocTypeName (type));
1402 root.SetAttribute("FullName", GetDocTypeFullName (type));
1404 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Language", "C#");
1405 WriteElementAttribute(root, "TypeSignature[@Language='C#']", "Value", MakeTypeSignature(type));
1407 XmlElement ass = WriteElement(root, "AssemblyInfo");
1408 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1409 UpdateAssemblyVersions(root, type, true);
1410 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1411 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1413 ClearElement(ass, "AssemblyCulture");
1415 // Why-oh-why do we put assembly attributes in each type file?
1416 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1417 // since they're outdated in current docs, and a waste of space.
1418 //MakeAttributes(ass, type.Assembly, true);
1419 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1420 if (assattrs != null)
1421 ass.RemoveChild(assattrs);
1423 NormalizeWhitespace(ass);
1425 if (type.IsGenericType ()) {
1426 MakeTypeParameters (root, type.GenericParameters);
1428 ClearElement(root, "TypeParameters");
1431 if (type.BaseType != null) {
1432 XmlElement basenode = WriteElement(root, "Base");
1434 string basetypename = GetDocTypeFullName (type.BaseType);
1435 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1436 WriteElementText(root, "Base/BaseTypeName", basetypename);
1438 // Document how this type instantiates the generic parameters of its base type
1439 TypeReference origBase = type.BaseType.GetOriginalType ();
1440 if (origBase.IsGenericType ()) {
1441 ClearElement(basenode, "BaseTypeArguments");
1442 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1443 GenericArgumentCollection baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1444 GenericParameterCollection baseGenParams = origBase.GenericParameters;
1445 if (baseGenArgs.Count != baseGenParams.Count)
1446 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1447 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1448 GenericParameter param = baseGenParams [i];
1449 TypeReference value = baseGenArgs [i];
1451 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1452 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1453 bta.AppendChild(arg);
1454 arg.SetAttribute ("TypeParamName", param.Name);
1455 arg.InnerText = GetDocTypeFullName (value);
1459 ClearElement(root, "Base");
1462 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1463 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1464 List<string> interface_names = userInterfaces
1465 .Select (iface => GetDocTypeFullName (iface))
1469 XmlElement interfaces = WriteElement(root, "Interfaces");
1470 interfaces.RemoveAll();
1471 foreach (string iname in interface_names) {
1472 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1473 interfaces.AppendChild(iface);
1474 WriteElementText(iface, "InterfaceName", iname);
1477 ClearElement(root, "Interfaces");
1480 MakeAttributes (root, type.CustomAttributes, false);
1482 if (DocUtils.IsDelegate (type)) {
1483 MakeTypeParameters (root, type.GenericParameters);
1484 MakeParameters(root, type.GetMethod("Invoke").Parameters);
1485 MakeReturnValue(root, type.GetMethod("Invoke"));
1488 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1489 if (ecmaDocsType != null) {
1490 if (ecmaDocsType.Name != "Docs") {
1491 int depth = ecmaDocsType.Depth;
1492 while (ecmaDocsType.Read ()) {
1493 if (ecmaDocsType.Name == "Docs" && ecmaDocsType.Depth == depth + 1)
1497 if (!ecmaDocsType.IsStartElement ("Docs"))
1498 throw new InvalidOperationException ("Found " + ecmaDocsType.Name + "; expecting <Docs/>!");
1499 typeInfo.EcmaDocs = ecmaDocsType;
1501 MakeDocNode (typeInfo);
1503 if (!DocUtils.IsDelegate (type))
1504 WriteElement (root, "Members");
1506 NormalizeWhitespace(root);
1509 static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1511 List<T> l = new List<T> (list);
1516 private static void UpdateMember (DocsNodeInfo info)
1518 XmlElement me = (XmlElement) info.Node;
1519 IMemberReference mi = info.Member;
1520 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Language", "C#");
1521 WriteElementAttribute(me, "MemberSignature[@Language='C#']", "Value", MakeMemberSignature(mi));
1523 WriteElementText(me, "MemberType", GetMemberType(mi));
1525 UpdateAssemblyVersions(me, mi, true);
1526 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1528 MakeAttributes (me, p.CustomAttributes, false);
1529 MakeReturnValue(me, mi);
1530 if (mi is MethodReference) {
1531 MethodReference mb = (MethodReference) mi;
1532 if (mb.IsGenericMethod ())
1533 MakeTypeParameters (me, mb.GenericParameters);
1535 MakeParameters(me, mi);
1538 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1539 WriteElementText(me, "MemberValue", fieldValue);
1541 info.Node = WriteElement (me, "Docs");
1543 UpdateExtensionMethods (me, info);
1546 static readonly string[] ValidExtensionMembers = {
1555 static readonly string[] ValidExtensionDocMembers = {
1561 private static void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1563 MethodDefinition me = info.Member as MethodDefinition;
1566 if (info.Parameters.Count < 1)
1568 if (!DocUtils.IsExtensionMethod (me))
1571 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1572 XmlNode member = e.CloneNode (true);
1573 em.AppendChild (member);
1574 RemoveExcept (member, ValidExtensionMembers);
1575 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1576 WriteElementText (member, "MemberType", "ExtensionMethod");
1577 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1578 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1579 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1580 member.AppendChild (link);
1581 AddTargets (em, info);
1583 extensionMethods.Add (em);
1586 private static void RemoveExcept (XmlNode node, string[] except)
1590 MyXmlNodeList remove = null;
1591 foreach (XmlNode n in node.ChildNodes) {
1592 if (Array.BinarySearch (except, n.Name) < 0) {
1594 remove = new MyXmlNodeList ();
1599 foreach (XmlNode n in remove)
1600 node.RemoveChild (n);
1603 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1605 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1606 member.PrependChild (targets);
1607 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1608 AppendElementAttributeText (targets, "Target", "Type",
1609 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1612 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1613 ConstraintCollection constraints = gp.Constraints;
1614 if (constraints.Count == 0)
1615 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1617 foreach (TypeReference c in constraints)
1618 AppendElementAttributeText(targets, "Target", "Type",
1619 slashdocFormatter.GetDeclaration (c));
1623 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1626 TypeDefinition type = DocUtils.GetTypeDefinition (field.DeclaringType);
1627 if (type != null && type.IsEnum) return false;
1629 if (type != null && type.IsGenericType ()) return false;
1630 if (!field.HasConstant)
1632 if (field.IsLiteral) {
1633 object val = field.Constant;
1634 if (val == null) value = "null";
1635 else if (val is Enum) value = val.ToString();
1636 else if (val is IFormattable) {
1637 value = ((IFormattable)val).ToString();
1639 value = "\"" + value + "\"";
1641 if (value != null && value != "")
1647 // XML HELPER FUNCTIONS
1649 private static XmlElement WriteElement(XmlNode parent, string element) {
1650 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1652 string[] path = element.Split('/');
1653 foreach (string p in path) {
1654 ret = (XmlElement)parent.SelectSingleNode(p);
1657 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1658 ename = ename.Substring(0, ename.IndexOf('['));
1659 ret = parent.OwnerDocument.CreateElement(ename);
1660 parent.AppendChild(ret);
1669 private static void WriteElementText(XmlNode parent, string element, string value) {
1670 XmlElement node = WriteElement(parent, element);
1671 node.InnerText = value;
1674 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1676 XmlElement n = parent.OwnerDocument.CreateElement (element);
1677 parent.AppendChild (n);
1678 n.InnerText = value;
1682 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1684 XmlElement n = parent.OwnerDocument.CreateElement (element);
1685 parent.AppendChild (n);
1686 n.SetAttribute (attribute, value);
1690 private static XmlNode CopyNode (XmlNode source, XmlNode dest)
1692 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1693 dest.AppendChild (copy);
1697 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1698 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1701 node = WriteElement(parent, element);
1702 node.InnerText = value;
1704 private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
1705 XmlElement node = WriteElement(parent, element);
1706 if (node.GetAttribute(attribute) == value) return;
1707 node.SetAttribute(attribute, value);
1709 private static void ClearElement(XmlElement parent, string name) {
1710 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1712 parent.RemoveChild(node);
1715 // DOCUMENTATION HELPER FUNCTIONS
1717 private static void MakeDocNode (DocsNodeInfo info)
1719 List<GenericParameter> genericParams = info.GenericParameters;
1720 ParameterDefinitionCollection parameters = info.Parameters;
1721 TypeReference returntype = info.ReturnType;
1722 bool returnisreturn = info.ReturnIsReturn;
1723 XmlElement e = info.Node;
1724 bool addremarks = info.AddRemarks;
1726 WriteElementInitialText(e, "summary", "To be added.");
1728 if (parameters != null) {
1729 string[] values = new string [parameters.Count];
1730 for (int i = 0; i < values.Length; ++i)
1731 values [i] = parameters [i].Name;
1732 UpdateParameters (e, "param", values);
1735 if (genericParams != null) {
1736 string[] values = new string [genericParams.Count];
1737 for (int i = 0; i < values.Length; ++i)
1738 values [i] = genericParams [i].Name;
1739 UpdateParameters (e, "typeparam", values);
1742 string retnodename = null;
1743 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
1744 retnodename = returnisreturn ? "returns" : "value";
1745 string retnodename_other = !returnisreturn ? "returns" : "value";
1747 // If it has a returns node instead of a value node, change its name.
1748 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1749 if (retother != null) {
1750 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1751 foreach (XmlNode node in retother)
1752 retnode.AppendChild(node.CloneNode(true));
1753 e.ReplaceChild(retnode, retother);
1755 WriteElementInitialText(e, retnodename, "To be added.");
1758 ClearElement(e, "returns");
1759 ClearElement(e, "value");
1763 WriteElementInitialText(e, "remarks", "To be added.");
1765 if (exceptions.HasValue && info.Member != null) {
1766 UpdateExceptions (e, info.Member);
1769 if (info.EcmaDocs != null) {
1770 XmlReader r = info.EcmaDocs;
1771 int depth = r.Depth;
1772 r.ReadStartElement ("Docs");
1774 if (r.Name == "Docs") {
1775 if (r.Depth == depth && r.NodeType == XmlNodeType.EndElement)
1778 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
1780 if (!r.IsStartElement ())
1785 XmlNode doc = e.SelectSingleNode (
1786 r.Name + "[@name='" + r.GetAttribute ("name") + "']");
1787 string value = r.ReadInnerXml ();
1789 doc.InnerXml = value.Replace ("\r", "");
1796 string name = r.Name;
1797 string cref = r.GetAttribute ("cref");
1798 XmlNode doc = e.SelectSingleNode (
1799 r.Name + "[@cref='" + cref + "']");
1800 string value = r.ReadInnerXml ().Replace ("\r", "");
1802 doc.InnerXml = value;
1804 XmlElement n = e.OwnerDocument.CreateElement (name);
1805 n.SetAttribute ("cref", cref);
1812 string name = r.Name;
1813 string xpath = r.Name;
1814 StringList attributes = new StringList (r.AttributeCount);
1815 if (r.MoveToFirstAttribute ()) {
1817 attributes.Add ("@" + r.Name + "=\"" + r.Value + "\"");
1818 } while (r.MoveToNextAttribute ());
1821 if (attributes.Count > 0) {
1822 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
1824 XmlNode doc = e.SelectSingleNode (xpath);
1825 string value = r.ReadInnerXml ().Replace ("\r", "");
1827 doc.InnerXml = value;
1830 XmlElement n = e.OwnerDocument.CreateElement (name);
1832 foreach (string a in attributes) {
1833 int eq = a.IndexOf ('=');
1834 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
1843 if (info.SlashDocs != null) {
1844 XmlNode elem = info.SlashDocs;
1846 if (elem.SelectSingleNode("summary") != null)
1847 ClearElement(e, "summary");
1848 if (elem.SelectSingleNode("remarks") != null)
1849 ClearElement(e, "remarks");
1850 if (elem.SelectSingleNode("value") != null)
1851 ClearElement(e, "value");
1852 if (retnodename != null && elem.SelectSingleNode(retnodename) != null)
1853 ClearElement(e, retnodename);
1855 foreach (XmlNode child in elem.ChildNodes) {
1856 switch (child.Name) {
1859 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + child.Attributes ["name"].Value + "']");
1861 p2.InnerXml = child.InnerXml;
1866 case "permission": {
1867 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + child.Attributes ["cref"].Value + "']");
1869 a = e.OwnerDocument.CreateElement (child.Name);
1870 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
1873 a.InnerXml = child.InnerXml;
1877 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + child.Attributes ["cref"].Value + "']");
1879 a = e.OwnerDocument.CreateElement ("altmember");
1880 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
1886 CopyNode (child, e);
1893 OrderDocsNodes (e, e.ChildNodes);
1894 NormalizeWhitespace(e);
1897 static readonly string[] DocsNodeOrder = {
1898 "typeparam", "param", "summary", "returns", "value", "remarks", "exception",
1901 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
1903 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1904 for (int i = 0; i < DocsNodeOrder.Length; ++i) {
1905 for (int j = 0; j < children.Count; ++j) {
1906 XmlNode c = children [j];
1907 if (c.Name == DocsNodeOrder [i]) {
1908 newChildren.Add (c);
1912 if (newChildren.Count >= 0)
1913 docs.PrependChild ((XmlNode) newChildren [0]);
1914 for (int i = 1; i < newChildren.Count; ++i) {
1915 XmlNode prev = (XmlNode) newChildren [i-1];
1916 XmlNode cur = (XmlNode) newChildren [i];
1917 docs.RemoveChild (cur);
1918 docs.InsertAfter (cur, prev);
1923 private static void UpdateParameters (XmlElement e, string element, string[] values)
1925 if (values != null) {
1926 XmlNode[] paramnodes = new XmlNode[values.Length];
1928 // Some documentation had param nodes with leading spaces.
1929 foreach (XmlElement paramnode in e.SelectNodes(element)){
1930 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
1933 // If a member has only one parameter, we can track changes to
1934 // the name of the parameter easily.
1935 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
1936 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
1939 bool reinsert = false;
1941 // Pick out existing and still-valid param nodes, and
1942 // create nodes for parameters not in the file.
1943 Hashtable seenParams = new Hashtable();
1944 for (int pi = 0; pi < values.Length; pi++) {
1945 string p = values [pi];
1948 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
1949 if (paramnodes[pi] != null) continue;
1951 XmlElement pe = e.OwnerDocument.CreateElement(element);
1952 pe.SetAttribute("name", p);
1953 pe.InnerText = "To be added.";
1954 paramnodes[pi] = pe;
1958 // Remove parameters that no longer exist and check all params are in the right order.
1960 MyXmlNodeList todelete = new MyXmlNodeList ();
1961 foreach (XmlElement paramnode in e.SelectNodes(element)) {
1962 string name = paramnode.GetAttribute("name");
1963 if (!seenParams.ContainsKey(name)) {
1964 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1965 Error ("The following param node can only be deleted if the --delete option is given: ");
1966 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
1968 Error ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
1969 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1973 Error ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
1974 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1975 e.ParentNode.Attributes ["MemberName"].Value,
1978 Error ("\tValue={0}", paramnode.OuterXml);
1980 todelete.Add (paramnode);
1985 if ((int)seenParams[name] != idx)
1991 foreach (XmlNode n in todelete) {
1992 n.ParentNode.RemoveChild (n);
1995 // Re-insert the parameter nodes at the top of the doc section.
1997 for (int pi = values.Length-1; pi >= 0; pi--)
1998 e.PrependChild(paramnodes[pi]);
2000 // Clear all existing param nodes
2001 foreach (XmlNode paramnode in e.SelectNodes(element)) {
2002 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2003 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
2004 Console.WriteLine(paramnode.OuterXml);
2006 paramnode.ParentNode.RemoveChild(paramnode);
2012 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
2014 string existingName = pe.GetAttribute ("name");
2015 pe.SetAttribute ("name", newName);
2016 if (existingName == newName)
2018 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
2019 if (paramref.GetAttribute ("name").Trim () == existingName)
2020 paramref.SetAttribute ("name", newName);
2023 private static void UpdateExceptions (XmlNode docs, IMemberReference member)
2025 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
2026 string cref = slashdocFormatter.GetDeclaration (source.Exception);
2027 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
2030 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
2031 e.SetAttribute ("cref", cref);
2032 e.InnerXml = "To be added; from: <see cref=\"" +
2033 string.Join ("\" />, <see cref=\"",
2034 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
2037 docs.AppendChild (e);
2039 SortXmlNodes (docs, docs.SelectNodes ("exception"),
2040 new AttributeNameComparer ("cref"));
2043 private static void NormalizeWhitespace(XmlElement e) {
2044 // Remove all text and whitespace nodes from the element so it
2045 // is outputted with nice indentation and no blank lines.
2046 ArrayList deleteNodes = new ArrayList();
2047 foreach (XmlNode n in e)
2048 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2050 foreach (XmlNode n in deleteNodes)
2051 n.ParentNode.RemoveChild(n);
2054 private static bool UpdateAssemblyVersions (XmlElement root, IMemberReference member, bool add)
2056 TypeDefinition type = member as TypeDefinition;
2058 type = member.DeclaringType as TypeDefinition;
2059 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
2062 private static string GetAssemblyVersion (AssemblyDefinition assembly)
2064 return assembly.Name.Version.ToString();
2067 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
2069 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
2071 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2072 root.AppendChild(e);
2074 MyXmlNodeList matches = new MyXmlNodeList (assemblyVersions.Length);
2075 foreach (XmlElement v in e.SelectNodes ("AssemblyVersion")) {
2076 foreach (string sv in assemblyVersions)
2077 if (v.InnerText == sv)
2080 // matches.Count > 0 && add: ignore -- already present
2081 if (matches.Count > 0 && !add) {
2082 foreach (XmlNode c in matches)
2085 else if (matches.Count == 0 && add) {
2086 foreach (string sv in assemblyVersions) {
2087 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2092 // matches.Count == 0 && !add: ignore -- already not present
2094 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2095 SortXmlNodes (e, avs, new VersionComparer ());
2097 return avs.Count != 0;
2100 // FIXME: get TypeReferences instead of string comparison?
2101 private static string[] IgnorableAttributes = {
2102 // Security related attributes
2103 "System.Reflection.AssemblyKeyFileAttribute",
2104 "System.Reflection.AssemblyDelaySignAttribute",
2105 // Present in @RefType
2106 "System.Runtime.InteropServices.OutAttribute",
2107 // For naming the indexer to use when not using indexers
2108 "System.Reflection.DefaultMemberAttribute",
2109 // for decimal constants
2110 "System.Runtime.CompilerServices.DecimalConstantAttribute",
2111 // compiler generated code
2112 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
2113 // more compiler generated code, e.g. iterator methods
2114 "System.Diagnostics.DebuggerHiddenAttribute",
2115 "System.Runtime.CompilerServices.FixedBufferAttribute",
2116 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
2117 // extension methods
2118 "System.Runtime.CompilerServices.ExtensionAttribute",
2121 private static void MakeAttributes (XmlElement root, CustomAttributeCollection attributes, bool assemblyAttributes)
2123 if (attributes.Count == 0) {
2124 ClearElement(root, "Attributes");
2129 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2133 e = root.OwnerDocument.CreateElement("Attributes");
2135 foreach (CustomAttribute attribute in attributes.Cast<CustomAttribute> ()
2136 .OrderBy (ca => ca.Constructor.DeclaringType.FullName)) {
2137 if (!attribute.Resolve ()) {
2139 Error ("warning: could not resolve type {0}.",
2140 attribute.Constructor.DeclaringType.FullName);
2142 TypeDefinition attrType = attribute.Constructor.DeclaringType as TypeDefinition;
2143 if (attrType != null && !IsPublic (attrType))
2145 if (slashdocFormatter.GetName (attribute.Constructor.DeclaringType) == null)
2148 if (Array.IndexOf (IgnorableAttributes, attribute.Constructor.DeclaringType.FullName) >= 0)
2153 StringList fields = new StringList ();
2155 ParameterDefinitionCollection parameters = attribute.Constructor.Parameters;
2156 for (int i = 0; i < attribute.ConstructorParameters.Count; ++i) {
2157 fields.Add (MakeAttributesValueString (
2158 attribute.ConstructorParameters [i],
2159 parameters [i].ParameterType));
2162 (from de in attribute.Fields.Cast<DictionaryEntry> ()
2163 select new { Type=attribute.GetFieldType (de.Key.ToString ()), Name=de.Key, Value=de.Value })
2165 (from de in attribute.Properties.Cast<DictionaryEntry> ()
2166 select new { Type=attribute.GetPropertyType (de.Key.ToString ()), Name=de.Key, Value=de.Value }))
2167 .OrderBy (v => v.Name);
2168 foreach (var d in namedArgs)
2169 fields.Add (string.Format ("{0}={1}", d.Name,
2170 MakeAttributesValueString (d.Value, d.Type)));
2172 string a2 = String.Join(", ", fields.ToArray ());
2173 if (a2 != "") a2 = "(" + a2 + ")";
2175 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2178 string name = attribute.Constructor.DeclaringType.FullName;
2179 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
2180 WriteElementText(ae, "AttributeName", name + a2);
2183 if (b && e.ParentNode == null)
2184 root.AppendChild(e);
2186 ClearElement(root, "Attributes");
2188 NormalizeWhitespace(e);
2191 private static string MakeAttributesValueString (object v, TypeReference valueType)
2195 if (valueType.FullName == "System.Type")
2196 return "typeof(" + v.ToString () + ")";
2197 if (valueType.FullName == "System.String")
2198 return "\"" + v.ToString () + "\"";
2200 return (bool)v ? "true" : "false";
2201 TypeDefinition valueDef = DocUtils.GetTypeDefinition (valueType);
2202 if (valueDef == null || !valueDef.IsEnum)
2203 return v.ToString ();
2204 string typename = GetDocTypeFullName (valueType);
2205 var values = GetEnumerationValues (valueDef);
2206 ulong c = Convert.ToUInt64 (v);
2207 if (values.ContainsKey (c))
2208 return typename + "." + values [c];
2209 if (valueDef.CustomAttributes.Cast<CustomAttribute> ()
2210 .Any (ca => ca.Constructor.DeclaringType.FullName == "System.FlagsAttribute")) {
2211 return string.Join (" | ",
2212 (from i in values.Keys
2214 select typename + "." + values [i])
2217 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
2220 private static Dictionary<ulong, string> GetEnumerationValues (TypeDefinition type)
2223 (from f in type.Fields.Cast<FieldDefinition> ()
2224 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
2226 .ToDictionary (f => Convert.ToUInt64 (f.Constant), f => f.Name);
2229 private static void MakeParameters (XmlElement root, ParameterDefinitionCollection parameters)
2231 XmlElement e = WriteElement(root, "Parameters");
2233 foreach (ParameterDefinition p in parameters) {
2234 XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
2236 pe.SetAttribute("Name", p.Name);
2237 pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
2238 if (p.ParameterType is ReferenceType) {
2239 if (p.IsOut) pe.SetAttribute("RefType", "out");
2240 else pe.SetAttribute("RefType", "ref");
2242 MakeAttributes (pe, p.CustomAttributes, false);
2246 private static void MakeTypeParameters (XmlElement root, GenericParameterCollection typeParams)
2248 if (typeParams == null || typeParams.Count == 0) {
2249 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2251 root.RemoveChild (f);
2254 XmlElement e = WriteElement(root, "TypeParameters");
2256 foreach (GenericParameter t in typeParams) {
2257 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2259 pe.SetAttribute("Name", t.Name);
2260 MakeAttributes (pe, t.CustomAttributes, false);
2261 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2262 ConstraintCollection constraints = t.Constraints;
2263 GenericParameterAttributes attrs = t.Attributes;
2264 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2272 ce = root.OwnerDocument.CreateElement ("Constraints");
2274 pe.AppendChild (ce);
2275 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2276 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2277 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2278 AppendElementText (ce, "ParameterAttribute", "Covariant");
2279 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2280 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2281 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2282 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2283 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2284 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2285 foreach (TypeReference c in constraints) {
2286 TypeDefinition cd = DocUtils.GetTypeDefinition (c);
2287 AppendElementText (ce,
2288 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2289 GetDocTypeFullName (c));
2294 private static void MakeParameters (XmlElement root, IMemberReference mi)
2296 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2297 MakeParameters (root, ((MethodDefinition)mi).Parameters);
2298 else if (mi is MethodDefinition) {
2299 MethodDefinition mb = (MethodDefinition) mi;
2300 ParameterDefinitionCollection parameters = mb.Parameters;
2301 MakeParameters(root, parameters);
2302 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2303 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2304 p.SetAttribute ("RefType", "this");
2307 else if (mi is PropertyDefinition) {
2308 ParameterDefinitionCollection parameters = ((PropertyDefinition)mi).Parameters;
2309 if (parameters.Count > 0)
2310 MakeParameters(root, parameters);
2314 else if (mi is FieldDefinition) return;
2315 else if (mi is EventDefinition) return;
2316 else throw new ArgumentException();
2319 private static string GetDocParameterType (TypeReference type)
2321 return GetDocTypeFullName (type).Replace ("@", "&");
2324 private static void MakeReturnValue(XmlElement root, TypeReference type, CustomAttributeCollection attributes)
2326 XmlElement e = WriteElement(root, "ReturnValue");
2328 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2329 if (attributes != null)
2330 MakeAttributes(e, attributes, false);
2333 private static void MakeReturnValue (XmlElement root, IMemberReference mi)
2335 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2337 else if (mi is MethodDefinition)
2338 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType.ReturnType, ((MethodDefinition)mi).ReturnType.CustomAttributes);
2339 else if (mi is PropertyDefinition)
2340 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
2341 else if (mi is FieldDefinition)
2342 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
2343 else if (mi is EventDefinition)
2344 MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
2346 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2349 private static XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info) {
2350 IMemberReference mi = info.Member;
2351 if (mi is TypeDefinition) return null;
2353 string sigs = MakeMemberSignature(mi);
2354 if (sigs == null) return null; // not publicly visible
2356 // no documentation for property/event accessors. Is there a better way of doing this?
2357 if (mi.Name.StartsWith("get_")) return null;
2358 if (mi.Name.StartsWith("set_")) return null;
2359 if (mi.Name.StartsWith("add_")) return null;
2360 if (mi.Name.StartsWith("remove_")) return null;
2361 if (mi.Name.StartsWith("raise_")) return null;
2363 XmlElement me = doc.CreateElement("Member");
2364 me.SetAttribute("MemberName", GetMemberName (mi));
2369 if (since != null) {
2370 XmlNode docs = me.SelectSingleNode("Docs");
2371 docs.AppendChild (CreateSinceNode (doc));
2377 private static string GetMemberName (IMemberReference mi)
2379 MethodDefinition mb = mi as MethodDefinition;
2381 PropertyDefinition pi = mi as PropertyDefinition;
2384 return DocUtils.GetPropertyName (pi);
2386 StringBuilder sb = new StringBuilder (mi.Name.Length);
2387 if (!DocUtils.IsExplicitlyImplemented (mb))
2388 sb.Append (mi.Name);
2390 TypeReference iface;
2391 MethodReference ifaceMethod;
2392 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2393 sb.Append (GetDocTypeFullName (iface));
2395 sb.Append (ifaceMethod.Name);
2397 if (mb.IsGenericMethod ()) {
2398 GenericParameterCollection typeParams = mb.GenericParameters;
2399 if (typeParams.Count > 0) {
2401 sb.Append (typeParams [0].Name);
2402 for (int i = 1; i < typeParams.Count; ++i)
2403 sb.Append (",").Append (typeParams [i].Name);
2407 return sb.ToString ();
2410 private static int CountChars (string s, char c)
2413 for (int i = 0; i < s.Length; ++i) {
2420 /// SIGNATURE GENERATION FUNCTIONS
2422 static string MakeTypeSignature (TypeReference type)
2424 return csharpFormatter.GetDeclaration (type);
2427 static string MakeMemberSignature (IMemberReference mi)
2429 return csharpFullFormatter.GetDeclaration (mi);
2432 static string GetMemberType (IMemberReference mi)
2434 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2435 return "Constructor";
2436 if (mi is MethodDefinition)
2438 if (mi is PropertyDefinition)
2440 if (mi is FieldDefinition)
2442 if (mi is EventDefinition)
2444 throw new ArgumentException();
2447 private static string GetDocTypeName (TypeReference type)
2449 return docTypeFormatter.GetName (type);
2452 private static string GetDocTypeFullName (TypeReference type)
2454 return DocTypeFullMemberFormatter.Default.GetName (type);
2457 class DocsNodeInfo {
2458 public DocsNodeInfo (XmlElement node)
2463 public DocsNodeInfo (XmlElement node, TypeDefinition type)
2469 public DocsNodeInfo (XmlElement node, IMemberReference member)
2472 SetMemberInfo (member);
2475 public void SetType (TypeDefinition type)
2478 throw new ArgumentNullException ("type");
2479 GenericParameters = new List<GenericParameter> (type.GenericParameters.Cast<GenericParameter> ());
2480 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
2481 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
2482 for (int i = 0; i < declTypes.Count - 1; ++i) {
2483 int remove = System.Math.Min (maxGenArgs,
2484 DocUtils.GetGenericArgumentCount (declTypes [i]));
2485 maxGenArgs -= remove;
2486 while (remove-- > 0)
2487 GenericParameters.RemoveAt (0);
2489 if (DocUtils.IsDelegate (type)) {
2490 Parameters = type.GetMethod("Invoke").Parameters;
2491 ReturnType = type.GetMethod("Invoke").ReturnType.ReturnType;
2493 SetSlashDocs (type);
2496 public void SetMemberInfo (IMemberReference member)
2499 throw new ArgumentNullException ("member");
2500 ReturnIsReturn = true;
2504 if (member is MethodReference ) {
2505 MethodReference mr = (MethodReference) member;
2506 Parameters = mr.Parameters;
2507 if (mr.IsGenericMethod ()) {
2508 GenericParameters = new List<GenericParameter> (mr.GenericParameters.Cast<GenericParameter> ());
2511 else if (member is PropertyDefinition) {
2512 Parameters = ((PropertyDefinition) member).Parameters;
2515 if (member is MethodDefinition) {
2516 ReturnType = ((MethodDefinition) member).ReturnType.ReturnType;
2517 } else if (member is PropertyDefinition) {
2518 ReturnType = ((PropertyDefinition) member).PropertyType;
2519 ReturnIsReturn = false;
2522 // no remarks section for enum members
2523 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
2525 SetSlashDocs (member);
2528 private void SetSlashDocs (IMemberReference member)
2530 if (slashdocs == null)
2533 string slashdocsig = slashdocFormatter.GetDeclaration (member);
2534 if (slashdocsig != null)
2535 SlashDocs = slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
2538 public TypeReference ReturnType;
2539 public List<GenericParameter> GenericParameters;
2540 public ParameterDefinitionCollection Parameters;
2541 public bool ReturnIsReturn;
2542 public XmlElement Node;
2543 public bool AddRemarks = true;
2544 public XmlNode SlashDocs;
2545 public XmlReader EcmaDocs;
2546 public IMemberReference Member;
2549 static string GetXPathForMember (DocumentationMember member)
2551 StringBuilder xpath = new StringBuilder ();
2552 xpath.Append ("//Members/Member[@MemberName=\"")
2553 .Append (member.MemberName)
2555 if (member.Parameters != null && member.Parameters.Count > 0) {
2556 xpath.Append ("/Parameters[count(Parameter) = ")
2557 .Append (member.Parameters.Count);
2558 for (int i = 0; i < member.Parameters.Count; ++i) {
2559 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2560 xpath.Append (member.Parameters [i]);
2561 xpath.Append ("\"");
2563 xpath.Append ("]/..");
2565 return xpath.ToString ();
2568 public static string GetXPathForMember (XPathNavigator member)
2570 StringBuilder xpath = new StringBuilder ();
2571 xpath.Append ("//Type[@FullName=\"")
2572 .Append (member.SelectSingleNode ("../../@FullName").Value)
2574 xpath.Append ("Members/Member[@MemberName=\"")
2575 .Append (member.SelectSingleNode ("@MemberName").Value)
2577 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2578 if (parameters.Count > 0) {
2579 xpath.Append ("/Parameters[count(Parameter) = ")
2580 .Append (parameters.Count);
2582 while (parameters.MoveNext ()) {
2584 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2585 xpath.Append (parameters.Current.Value);
2586 xpath.Append ("\"");
2588 xpath.Append ("]/..");
2590 return xpath.ToString ();
2593 public static string GetXPathForMember (IMemberReference member)
2595 StringBuilder xpath = new StringBuilder ();
2596 xpath.Append ("//Type[@FullName=\"")
2597 .Append (member.DeclaringType.FullName)
2599 xpath.Append ("Members/Member[@MemberName=\"")
2600 .Append (GetMemberName (member))
2603 ParameterDefinitionCollection parameters = null;
2604 if (member is MethodDefinition)
2605 parameters = ((MethodDefinition) member).Parameters;
2606 else if (member is PropertyDefinition) {
2607 parameters = ((PropertyDefinition) member).Parameters;
2609 if (parameters != null && parameters.Count > 0) {
2610 xpath.Append ("/Parameters[count(Parameter) = ")
2611 .Append (parameters.Count);
2612 for (int i = 0; i < parameters.Count; ++i) {
2613 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2614 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2615 xpath.Append ("\"");
2617 xpath.Append ("]/..");
2619 return xpath.ToString ();
2623 static class CecilExtensions {
2624 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type)
2626 foreach (var c in type.Constructors)
2627 yield return (IMemberReference) c;
2628 foreach (var e in type.Events)
2629 yield return (IMemberReference) e;
2630 foreach (var f in type.Fields)
2631 yield return (IMemberReference) f;
2632 foreach (var m in type.Methods)
2633 yield return (IMemberReference) m;
2634 foreach (var t in type.NestedTypes)
2635 yield return (IMemberReference) t;
2636 foreach (var p in type.Properties)
2637 yield return (IMemberReference) p;
2640 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type, string member)
2642 return GetMembers (type).Where (m => m.Name == member);
2645 public static IMemberReference GetMember (this TypeDefinition type, string member)
2647 return GetMembers (type, member).EnsureZeroOrOne ();
2650 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2652 if (source.Count () > 1)
2653 throw new InvalidOperationException ("too many matches");
2654 return source.FirstOrDefault ();
2657 static T EnsureOne<T> (this IEnumerable<T> source)
2659 if (source.Count () > 1)
2660 throw new InvalidOperationException ("too many matches: " +
2661 string.Join ("; ", source.Select (e => e.ToString ()).ToArray ()));
2662 return source.First ();
2665 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2667 return type.Methods.Cast<MethodDefinition> ()
2668 .Where (m => m.Name == method)
2669 .EnsureZeroOrOne ();
2672 public static IEnumerable<IMemberReference> GetDefaultMembers (this TypeReference type)
2674 TypeDefinition def = type as TypeDefinition;
2676 return new IMemberReference [0];
2677 CustomAttribute defMemberAttr = type.CustomAttributes.Cast<CustomAttribute> ()
2678 .Where (c => c.Constructor.DeclaringType.FullName == "System.Reflection.DefaultMemberAttribute")
2680 if (defMemberAttr == null)
2681 return new IMemberReference [0];
2682 string name = (string) defMemberAttr.ConstructorParameters [0];
2683 return def.Properties.Cast<PropertyDefinition> ()
2684 .Where (p => p.Name == name)
2685 .Select (p => (IMemberReference) p);
2688 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2690 return assembly.Modules.Cast<ModuleDefinition> ()
2691 .SelectMany (md => md.Types.Cast<TypeDefinition> ());
2694 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2696 return GetTypes (assembly)
2697 .Where (td => td.FullName == type)
2698 .EnsureZeroOrOne ();
2701 public static bool IsGenericType (this TypeReference type)
2703 return type.GenericParameters.Count > 0;
2706 public static bool IsGenericMethod (this MethodReference method)
2708 return method.GenericParameters.Count > 0;
2711 public static IMemberReference GetDefinition (this IMemberReference member)
2713 EventReference er = member as EventReference;
2715 return GetEventDefinition (er);
2716 FieldReference fr = member as FieldReference;
2718 return GetFieldDefinition (fr);
2719 MethodReference mr = member as MethodReference;
2721 return GetMethodDefinition (mr);
2722 PropertyReference pr = member as PropertyReference;
2724 return GetPropertyDefinition (pr);
2725 TypeReference tr = member as TypeReference;
2727 return GetTypeDefinition (tr);
2728 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2731 public static EventDefinition GetEventDefinition (this EventReference ev)
2733 EventDefinition evDef = ev as EventDefinition;
2736 return (EventDefinition) ev.DeclaringType.GetTypeDefinition ()
2737 .GetMember (ev.Name);
2740 public static FieldDefinition GetFieldDefinition (this FieldReference field)
2742 FieldDefinition fieldDef = field as FieldDefinition;
2743 if (fieldDef != null)
2745 return (FieldDefinition) field.DeclaringType.GetTypeDefinition ()
2746 .GetMember (field.Name);
2749 public static MethodDefinition GetMethodDefinition (this MethodReference method)
2751 MethodDefinition methodDef = method as MethodDefinition;
2752 if (methodDef != null)
2754 method = method.GetOriginalMethod ();
2755 return method.DeclaringType.GetTypeDefinition ()
2756 .GetMembers (method.Name).OfType<MethodDefinition> ()
2758 AreSame (method.ReturnType.ReturnType, m.ReturnType.ReturnType) &&
2759 AreSame (method.Parameters, m.Parameters))
2763 static bool AreSame (ParameterDefinitionCollection a, ParameterDefinitionCollection b)
2765 if (a.Count != b.Count)
2771 for (int i = 0; i < a.Count; i++) {
2772 if (!AreSame (a [i].ParameterType, b [i].ParameterType))
2779 static bool AreSame (TypeReference a, TypeReference b)
2781 while (a is TypeSpecification || b is TypeSpecification) {
2782 if (a.GetType () != b.GetType ())
2785 IGenericInstance ga = a as IGenericInstance;
2786 IGenericInstance gb = b as IGenericInstance;
2788 a = ((TypeSpecification) a).ElementType;
2789 b = ((TypeSpecification) b).ElementType;
2791 if (ga != null && gb != null) {
2792 if (ga.GenericArguments.Count != gb.GenericArguments.Count) {
2795 for (int i = 0; i < ga.GenericArguments.Count; ++i) {
2796 if (!AreSame (ga.GenericArguments [i], gb.GenericArguments [i]))
2802 GenericParameter pa = (a as GenericParameter);
2803 GenericParameter pb = (b as GenericParameter);
2804 if ((pa != null) || (pb != null)) {
2805 if (a.GetType () != b.GetType ())
2808 return pa.Position == pb.Position;
2811 return a.FullName == b.FullName;
2814 public static PropertyDefinition GetPropertyDefinition (this PropertyReference property)
2816 PropertyDefinition propertyDef = property as PropertyDefinition;
2817 if (propertyDef != null)
2819 return (PropertyDefinition) property.DeclaringType.GetTypeDefinition ()
2820 .GetMembers (property.Name).OfType<PropertyDefinition> ()
2821 .Where (p => p.PropertyType.FullName == property.PropertyType.FullName &&
2822 AreSame (property.Parameters, p.Parameters))
2826 public static TypeDefinition GetTypeDefinition (this TypeReference type)
2828 return DocUtils.GetTypeDefinition (type);
2832 static class DocUtils {
2833 public static bool IsExplicitlyImplemented (MethodDefinition method)
2835 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2838 public static string GetTypeDotMember (string name)
2840 int startType, startMethod;
2841 startType = startMethod = -1;
2842 for (int i = 0; i < name.Length; ++i) {
2843 if (name [i] == '.') {
2844 startType = startMethod;
2848 return name.Substring (startType+1);
2851 public static string GetMember (string name)
2853 int i = name.LastIndexOf ('.');
2856 return name.Substring (i+1);
2859 public static void GetInfoForExplicitlyImplementedMethod (
2860 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2864 if (method.Overrides.Count != 1)
2865 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2866 iface = method.Overrides [0].DeclaringType;
2867 ifaceMethod = method.Overrides [0];
2870 public static string GetPropertyName (PropertyDefinition pi)
2872 // Issue: (g)mcs-generated assemblies that explicitly implement
2873 // properties don't specify the full namespace, just the
2874 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2875 MethodDefinition method = pi.GetMethod;
2877 method = pi.SetMethod;
2878 if (!IsExplicitlyImplemented (method))
2881 // Need to determine appropriate namespace for this member.
2882 TypeReference iface;
2883 MethodReference ifaceMethod;
2884 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2885 return string.Join (".", new string[]{
2886 DocTypeFullMemberFormatter.Default.GetName (iface),
2887 GetMember (pi.Name)});
2890 public static string GetNamespace (TypeReference type)
2892 if (type.GetOriginalType ().IsNested)
2893 type = type.GetOriginalType ();
2894 while (type != null && type.IsNested)
2895 type = type.DeclaringType;
2897 return string.Empty;
2898 return type.Namespace;
2901 public static string PathCombine (string dir, string path)
2907 return Path.Combine (dir, path);
2910 public static bool IsExtensionMethod (MethodDefinition method)
2913 method.CustomAttributes.Cast<CustomAttribute> ()
2914 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2916 method.DeclaringType.CustomAttributes.Cast<CustomAttribute> ()
2917 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2921 public static bool IsDelegate (TypeDefinition type)
2923 TypeReference baseRef = type.BaseType;
2924 if (baseRef == null)
2926 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2927 baseRef.FullName == "System.MulticastDelegate";
2930 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2932 List<TypeReference> decls = new List<TypeReference> ();
2934 while (type.DeclaringType != null) {
2935 decls.Add (type.DeclaringType);
2936 type = type.DeclaringType;
2942 public static int GetGenericArgumentCount (TypeReference type)
2944 GenericInstanceType inst = type as GenericInstanceType;
2946 ? inst.GenericArguments.Count
2947 : type.GenericParameters.Count;
2950 public static TypeDefinition GetTypeDefinition (TypeReference type)
2952 // Remove generic instantiation info (so string comparison below works)
2953 type = type.GetOriginalType ();
2954 TypeDefinition typeDef = type as TypeDefinition;
2955 if (typeDef != null)
2958 AssemblyNameReference reference = type.Scope as AssemblyNameReference;
2959 if (reference != null) {
2960 AssemblyDefinition ad = type.Module.Assembly.Resolver.Resolve (reference);
2961 if (ad != null && (typeDef = ad.MainModule.Types [type.FullName]) != null)
2964 ModuleDefinition module = type.Scope as ModuleDefinition;
2965 if (module != null && (typeDef = module.Types [type.FullName]) != null)
2970 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
2972 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
2973 List<TypeReference> userInterfaces = new List<TypeReference> ();
2974 foreach (TypeReference iface in type.Interfaces) {
2975 TypeReference lookup = GetTypeDefinition (iface) ?? iface;
2976 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
2977 userInterfaces.Add (iface);
2979 return userInterfaces;
2982 private static string GetQualifiedTypeName (TypeReference type)
2984 return "[" + type.Scope.Name + "]" + type.FullName;
2987 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
2989 HashSet<string> inheritedInterfaces = new HashSet<string> ();
2990 Action<TypeDefinition> a = null;
2992 if (t == null) return;
2993 foreach (TypeReference r in t.Interfaces) {
2994 inheritedInterfaces.Add (GetQualifiedTypeName (r));
2995 a (GetTypeDefinition (r));
2998 TypeReference baseRef = type.BaseType;
2999 while (baseRef != null) {
3000 TypeDefinition baseDef = GetTypeDefinition (baseRef);
3001 if (baseDef != null) {
3003 baseRef = baseDef.BaseType;
3008 foreach (TypeReference r in type.Interfaces)
3009 a (GetTypeDefinition (r));
3010 return inheritedInterfaces;
3014 class DocumentationMember {
3015 public StringToStringMap MemberSignatures = new StringToStringMap ();
3016 public string ReturnType;
3017 public StringList Parameters;
3018 public string MemberName;
3019 public string MemberType;
3021 public DocumentationMember (XmlReader reader)
3023 MemberName = reader.GetAttribute ("MemberName");
3024 int depth = reader.Depth;
3026 StringList p = new StringList ();
3028 if (reader.NodeType != XmlNodeType.Element)
3030 switch (reader.Name) {
3031 case "MemberSignature":
3032 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3035 MemberType = reader.ReadElementString ();
3038 if (reader.Depth == depth + 2)
3039 ReturnType = reader.ReadElementString ();
3042 if (reader.Depth == depth + 2)
3043 p.Add (reader.GetAttribute ("Type"));
3046 if (reader.Depth == depth + 1)
3050 } while (go && reader.Read () && reader.Depth >= depth);
3056 public DocumentationMember (XmlNode node)
3058 MemberName = node.Attributes ["MemberName"].Value;
3059 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3060 XmlAttribute l = n.Attributes ["Language"];
3061 XmlAttribute v = n.Attributes ["Value"];
3062 if (l != null && v != null)
3063 MemberSignatures [l.Value] = v.Value;
3065 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3066 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
3068 ReturnType = rt.InnerText;
3069 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
3071 Parameters = new StringList (p.Count);
3072 for (int i = 0; i < p.Count; ++i)
3073 Parameters.Add (p [i].Attributes ["Type"].Value);
3078 public abstract class MemberFormatter {
3079 public virtual string GetName (IMemberReference member)
3081 TypeReference type = member as TypeReference;
3083 return GetTypeName (type);
3084 MethodReference method = member as MethodReference;
3085 if (method != null && method.Name == ".ctor") // method.IsConstructor
3086 return GetConstructorName (method);
3088 return GetMethodName (method);
3089 PropertyReference prop = member as PropertyReference;
3091 return GetPropertyName (prop);
3092 FieldReference field = member as FieldReference;
3094 return GetFieldName (field);
3095 EventReference e = member as EventReference;
3097 return GetEventName (e);
3098 throw new NotSupportedException ("Can't handle: " +
3099 (member == null ? "null" : member.GetType().ToString()));
3102 protected virtual string GetTypeName (TypeReference type)
3105 throw new ArgumentNullException ("type");
3106 return _AppendTypeName (new StringBuilder (type.Name.Length), type).ToString ();
3109 protected virtual char[] ArrayDelimeters {
3110 get {return new char[]{'[', ']'};}
3113 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type)
3115 if (type is ArrayType) {
3116 TypeSpecification spec = type as TypeSpecification;
3117 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3118 .Append (ArrayDelimeters [0]);
3119 ArrayType array = (ArrayType) type;
3120 int rank = array.Rank;
3122 buf.Append (new string (',', rank-1));
3123 return buf.Append (ArrayDelimeters [1]);
3125 if (type is ReferenceType) {
3126 return AppendRefTypeName (buf, type);
3128 if (type is PointerType) {
3129 return AppendPointerTypeName (buf, type);
3131 AppendNamespace (buf, type);
3132 if (type is GenericParameter) {
3133 return AppendTypeName (buf, type);
3135 GenericInstanceType genInst = type as GenericInstanceType;
3136 if (type.GenericParameters.Count == 0 &&
3137 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3138 return AppendFullTypeName (buf, type);
3140 return AppendGenericType (buf, type);
3143 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3145 string ns = DocUtils.GetNamespace (type);
3146 if (ns != null && ns.Length > 0)
3147 buf.Append (ns).Append ('.');
3151 private StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type)
3153 if (type.DeclaringType != null)
3154 AppendFullTypeName (buf, type.DeclaringType).Append (NestedTypeSeparator);
3155 return AppendTypeName (buf, type);
3158 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3160 return AppendTypeName (buf, type.Name);
3163 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3165 int n = typename.IndexOf ("`");
3167 return buf.Append (typename.Substring (0, n));
3168 return buf.Append (typename);
3171 protected virtual string RefTypeModifier {
3175 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type)
3177 TypeSpecification spec = type as TypeSpecification;
3178 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3179 .Append (RefTypeModifier);
3182 protected virtual string PointerModifier {
3186 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type)
3188 TypeSpecification spec = type as TypeSpecification;
3189 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3190 .Append (PointerModifier);
3193 protected virtual char[] GenericTypeContainer {
3194 get {return new char[]{'<', '>'};}
3197 protected virtual char NestedTypeSeparator {
3201 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3203 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3204 type is GenericInstanceType ? type.GetOriginalType () : type);
3205 List<TypeReference> genArgs = GetGenericArguments (type);
3208 bool insertNested = false;
3209 foreach (var decl in decls) {
3210 TypeReference declDef = DocUtils.GetTypeDefinition (decl) ?? decl;
3212 buf.Append (NestedTypeSeparator);
3214 insertNested = true;
3215 AppendTypeName (buf, declDef);
3216 int ac = DocUtils.GetGenericArgumentCount (declDef);
3220 buf.Append (GenericTypeContainer [0]);
3221 _AppendTypeName (buf, genArgs [argIdx++]);
3222 for (int i = 1; i < c; ++i)
3223 _AppendTypeName (buf.Append (","), genArgs [argIdx++]);
3224 buf.Append (GenericTypeContainer [1]);
3230 private List<TypeReference> GetGenericArguments (TypeReference type)
3232 var args = new List<TypeReference> ();
3233 GenericInstanceType inst = type as GenericInstanceType;
3235 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
3237 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
3241 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3246 protected virtual string GetConstructorName (MethodReference constructor)
3248 return constructor.Name;
3251 protected virtual string GetMethodName (MethodReference method)
3256 protected virtual string GetPropertyName (PropertyReference property)
3258 return property.Name;
3261 protected virtual string GetFieldName (FieldReference field)
3266 protected virtual string GetEventName (EventReference e)
3271 public virtual string GetDeclaration (IMemberReference member)
3274 throw new ArgumentNullException ("member");
3275 TypeDefinition type = member as TypeDefinition;
3277 return GetTypeDeclaration (type);
3278 MethodDefinition method = member as MethodDefinition;
3279 if (method != null && method.IsConstructor)
3280 return GetConstructorDeclaration (method);
3282 return GetMethodDeclaration (method);
3283 PropertyDefinition prop = member as PropertyDefinition;
3285 return GetPropertyDeclaration (prop);
3286 FieldDefinition field = member as FieldDefinition;
3288 return GetFieldDeclaration (field);
3289 EventDefinition e = member as EventDefinition;
3291 return GetEventDeclaration (e);
3292 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3295 protected virtual string GetTypeDeclaration (TypeDefinition type)
3298 throw new ArgumentNullException ("type");
3299 StringBuilder buf = new StringBuilder (type.Name.Length);
3300 _AppendTypeName (buf, type);
3301 AppendGenericTypeConstraints (buf, type);
3302 return buf.ToString ();
3305 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
3307 return GetConstructorName (constructor);
3310 protected virtual string GetMethodDeclaration (MethodDefinition method)
3312 // Special signature for destructors.
3313 if (method.Name == "Finalize" && method.Parameters.Count == 0)
3314 return GetFinalizerName (method);
3316 StringBuilder buf = new StringBuilder ();
3318 AppendVisibility (buf, method);
3319 if (buf.Length == 0 &&
3320 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3323 AppendModifiers (buf, method);
3325 if (buf.Length != 0)
3327 buf.Append (GetName (method.ReturnType.ReturnType)).Append (" ");
3329 AppendMethodName (buf, method);
3330 AppendGenericMethod (buf, method).Append (" ");
3331 AppendParameters (buf, method, method.Parameters);
3332 AppendGenericMethodConstraints (buf, method);
3333 return buf.ToString ();
3336 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3338 return buf.Append (method.Name);
3341 protected virtual string GetFinalizerName (MethodDefinition method)
3346 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3351 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3356 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3361 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3366 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3371 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
3373 return GetPropertyName (property);
3376 protected virtual string GetFieldDeclaration (FieldDefinition field)
3378 return GetFieldName (field);
3381 protected virtual string GetEventDeclaration (EventDefinition e)
3383 return GetEventName (e);
3387 class CSharpFullMemberFormatter : MemberFormatter {
3389 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3391 string ns = DocUtils.GetNamespace (type);
3392 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
3393 buf.Append (ns).Append ('.');
3397 private string GetCSharpType (string t)
3400 case "System.Byte": return "byte";
3401 case "System.SByte": return "sbyte";
3402 case "System.Int16": return "short";
3403 case "System.Int32": return "int";
3404 case "System.Int64": return "long";
3406 case "System.UInt16": return "ushort";
3407 case "System.UInt32": return "uint";
3408 case "System.UInt64": return "ulong";
3410 case "System.Single": return "float";
3411 case "System.Double": return "double";
3412 case "System.Decimal": return "decimal";
3413 case "System.Boolean": return "bool";
3414 case "System.Char": return "char";
3415 case "System.Void": return "void";
3416 case "System.String": return "string";
3417 case "System.Object": return "object";
3422 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3424 if (type is GenericParameter)
3425 return buf.Append (type.Name);
3426 string t = type.FullName;
3427 if (!t.StartsWith ("System.")) {
3428 return base.AppendTypeName (buf, type);
3431 string s = GetCSharpType (t);
3433 return buf.Append (s);
3435 return base.AppendTypeName (buf, type);
3438 protected override string GetTypeDeclaration (TypeDefinition type)
3440 string visibility = GetTypeVisibility (type.Attributes);
3441 if (visibility == null)
3444 StringBuilder buf = new StringBuilder ();
3446 buf.Append (visibility);
3449 MemberFormatter full = new CSharpFullMemberFormatter ();
3451 if (DocUtils.IsDelegate (type)) {
3452 buf.Append("delegate ");
3453 MethodDefinition invoke = type.GetMethod ("Invoke");
3454 buf.Append (full.GetName (invoke.ReturnType.ReturnType)).Append (" ");
3455 buf.Append (GetName (type));
3456 AppendParameters (buf, invoke, invoke.Parameters);
3457 AppendGenericTypeConstraints (buf, type);
3460 return buf.ToString();
3463 if (type.IsAbstract && !type.IsInterface)
3464 buf.Append("abstract ");
3465 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
3466 buf.Append("sealed ");
3467 buf.Replace ("abstract sealed", "static");
3469 buf.Append (GetTypeKind (type));
3471 buf.Append (GetCSharpType (type.FullName) == null
3476 TypeReference basetype = type.BaseType;
3477 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
3480 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
3481 .Select (iface => full.GetName (iface))
3485 if (basetype != null || interface_names.Count > 0)
3488 if (basetype != null) {
3489 buf.Append (full.GetName (basetype));
3490 if (interface_names.Count > 0)
3494 for (int i = 0; i < interface_names.Count; i++){
3497 buf.Append (interface_names [i]);
3499 AppendGenericTypeConstraints (buf, type);
3502 return buf.ToString ();
3505 static string GetTypeKind (TypeDefinition t)
3511 if (t.IsClass || t.FullName == "System.Enum")
3515 throw new ArgumentException(t.FullName);
3518 static string GetTypeVisibility (TypeAttributes ta)
3520 switch (ta & TypeAttributes.VisibilityMask) {
3521 case TypeAttributes.Public:
3522 case TypeAttributes.NestedPublic:
3525 case TypeAttributes.NestedFamily:
3526 case TypeAttributes.NestedFamORAssem:
3534 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3536 if (type.GenericParameters.Count == 0)
3538 return AppendConstraints (buf, type.GenericParameters);
3541 private StringBuilder AppendConstraints (StringBuilder buf, GenericParameterCollection genArgs)
3543 foreach (GenericParameter genArg in genArgs) {
3544 GenericParameterAttributes attrs = genArg.Attributes;
3545 ConstraintCollection constraints = genArg.Constraints;
3546 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
3548 buf.Append (" where ").Append (genArg.Name).Append (" : ");
3549 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
3550 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
3551 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
3554 buf.Append ("class");
3558 buf.Append ("struct");
3561 if (constraints.Count > 0 && !isvt) {
3564 buf.Append (GetTypeName (constraints [0]));
3565 for (int i = 1; i < constraints.Count; ++i)
3566 buf.Append (", ").Append (GetTypeName (constraints [i]));
3568 if (isnew && !isvt) {
3571 buf.Append ("new()");
3577 protected override string GetConstructorDeclaration (MethodDefinition constructor)
3579 StringBuilder buf = new StringBuilder ();
3580 AppendVisibility (buf, constructor);
3581 if (buf.Length == 0)
3585 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
3586 AppendParameters (buf, constructor, constructor.Parameters);
3589 return buf.ToString ();
3592 protected override string GetMethodDeclaration (MethodDefinition method)
3594 string decl = base.GetMethodDeclaration (method);
3600 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3602 if (DocUtils.IsExplicitlyImplemented (method)) {
3603 TypeReference iface;
3604 MethodReference ifaceMethod;
3605 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3606 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3608 .Append (ifaceMethod.Name);
3610 return base.AppendMethodName (buf, method);
3613 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3615 if (method.GenericParameters.Count == 0)
3617 return AppendConstraints (buf, method.GenericParameters);
3620 protected override string RefTypeModifier {
3624 protected override string GetFinalizerName (MethodDefinition method)
3626 return "~" + method.DeclaringType.Name + " ()";
3629 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3633 if (method.IsPublic)
3634 return buf.Append ("public");
3635 if (method.IsFamily || method.IsFamilyOrAssembly)
3636 return buf.Append ("protected");
3640 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3642 string modifiers = String.Empty;
3643 if (method.IsStatic) modifiers += " static";
3644 if (method.IsVirtual && !method.IsAbstract) {
3645 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3646 else modifiers += " override";
3648 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
3649 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
3650 if (method.IsFinal) modifiers += " sealed";
3651 if (modifiers == " virtual sealed") modifiers = "";
3653 return buf.Append (modifiers);
3656 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3658 if (method.IsGenericMethod ()) {
3659 GenericParameterCollection args = method.GenericParameters;
3660 if (args.Count > 0) {
3662 buf.Append (args [0].Name);
3663 for (int i = 1; i < args.Count; ++i)
3664 buf.Append (",").Append (args [i].Name);
3671 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3673 return AppendParameters (buf, method, parameters, '(', ')');
3676 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters, char begin, char end)
3680 if (parameters.Count > 0) {
3681 if (DocUtils.IsExtensionMethod (method))
3682 buf.Append ("this ");
3683 AppendParameter (buf, parameters [0]);
3684 for (int i = 1; i < parameters.Count; ++i) {
3686 AppendParameter (buf, parameters [i]);
3690 return buf.Append (end);
3693 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
3695 if (parameter.ParameterType is ReferenceType) {
3696 if (parameter.IsOut)
3697 buf.Append ("out ");
3699 buf.Append ("ref ");
3701 buf.Append (GetName (parameter.ParameterType)).Append (" ");
3702 return buf.Append (parameter.Name);
3705 protected override string GetPropertyDeclaration (PropertyDefinition property)
3707 MethodDefinition method;
3709 string get_visible = null;
3710 if ((method = property.GetMethod) != null &&
3711 (DocUtils.IsExplicitlyImplemented (method) ||
3712 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3713 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3714 string set_visible = null;
3715 if ((method = property.SetMethod) != null &&
3716 (DocUtils.IsExplicitlyImplemented (method) ||
3717 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3718 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3720 if ((set_visible == null) && (get_visible == null))
3724 StringBuilder buf = new StringBuilder ();
3725 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
3726 buf.Append (visibility = get_visible);
3727 else if (set_visible != null && get_visible == null)
3728 buf.Append (visibility = set_visible);
3730 buf.Append (visibility = "public");
3732 // Pick an accessor to use for static/virtual/override/etc. checks.
3733 method = property.SetMethod;
3735 method = property.GetMethod;
3737 string modifiers = String.Empty;
3738 if (method.IsStatic) modifiers += " static";
3739 if (method.IsVirtual && !method.IsAbstract) {
3740 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
3741 modifiers += " virtual";
3743 modifiers += " override";
3745 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
3746 if (method.IsAbstract && !declDef.IsInterface)
3747 modifiers += " abstract";
3749 modifiers += " sealed";
3750 if (modifiers == " virtual sealed")
3752 buf.Append (modifiers).Append (' ');
3754 buf.Append (GetName (property.PropertyType)).Append (' ');
3756 IEnumerable<IMemberReference> defs = property.DeclaringType.GetDefaultMembers ();
3757 string name = property.Name;
3758 foreach (IMemberReference mi in defs) {
3759 if (mi == property) {
3764 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
3766 if (property.Parameters.Count != 0) {
3767 AppendParameters (buf, method, property.Parameters, '[', ']');
3771 if (set_visible != null) {
3772 if (set_visible != visibility)
3773 buf.Append (' ').Append (set_visible);
3774 buf.Append (" set;");
3776 if (get_visible != null) {
3777 if (get_visible != visibility)
3778 buf.Append (' ').Append (get_visible);
3779 buf.Append (" get;");
3783 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
3786 protected override string GetFieldDeclaration (FieldDefinition field)
3788 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
3789 if (declType.IsEnum && field.Name == "value__")
3790 return null; // This member of enums aren't documented.
3792 StringBuilder buf = new StringBuilder ();
3793 AppendFieldVisibility (buf, field);
3794 if (buf.Length == 0)
3797 if (declType.IsEnum)
3800 if (field.IsStatic && !field.IsLiteral)
3801 buf.Append (" static");
3802 if (field.IsInitOnly)
3803 buf.Append (" readonly");
3804 if (field.IsLiteral)
3805 buf.Append (" const");
3807 buf.Append (' ').Append (GetName (field.FieldType)).Append (' ');
3808 buf.Append (field.Name);
3809 AppendFieldValue (buf, field);
3812 return buf.ToString ();
3815 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
3818 return buf.Append ("public");
3819 if (field.IsFamily || field.IsFamilyOrAssembly)
3820 return buf.Append ("protected");
3824 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
3826 // enums have a value__ field, which we ignore
3827 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
3828 field.DeclaringType.IsGenericType ())
3830 if (field.HasConstant && field.IsLiteral) {
3833 val = field.Constant;
3838 buf.Append (" = ").Append ("null");
3839 else if (val is Enum)
3840 buf.Append (" = ").Append (val.ToString ());
3841 else if (val is IFormattable) {
3842 string value = ((IFormattable)val).ToString();
3844 value = "\"" + value + "\"";
3845 buf.Append (" = ").Append (value);
3851 protected override string GetEventDeclaration (EventDefinition e)
3853 StringBuilder buf = new StringBuilder ();
3854 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
3858 AppendModifiers (buf, e.AddMethod);
3860 buf.Append (" event ");
3861 buf.Append (GetName (e.EventType)).Append (' ');
3862 buf.Append (e.Name).Append (';');
3864 return buf.ToString ();
3868 class CSharpMemberFormatter : CSharpFullMemberFormatter {
3869 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3875 class DocTypeFullMemberFormatter : MemberFormatter {
3876 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
3878 protected override char NestedTypeSeparator {
3883 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
3884 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3890 class SlashDocMemberFormatter : MemberFormatter {
3892 protected override char[] GenericTypeContainer {
3893 get {return new char[]{'{', '}'};}
3896 private bool AddTypeCount = true;
3898 private TypeReference genDeclType;
3899 private MethodReference genDeclMethod;
3901 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3903 if (type is GenericParameter) {
3905 if (genDeclType != null) {
3906 GenericParameterCollection genArgs = genDeclType.GenericParameters;
3907 for (int i = 0; i < genArgs.Count; ++i) {
3908 if (genArgs [i].Name == type.Name) {
3909 buf.Append ('`').Append (i);
3914 if (genDeclMethod != null) {
3915 GenericParameterCollection genArgs = null;
3916 if (genDeclMethod.IsGenericMethod ()) {
3917 genArgs = genDeclMethod.GenericParameters;
3918 for (int i = 0; i < genArgs.Count; ++i) {
3919 if (genArgs [i].Name == type.Name) {
3920 buf.Append ("``").Append (i);
3926 if (genDeclType == null && genDeclMethod == null) {
3927 // Probably from within an explicitly implemented interface member,
3928 // where CSC uses parameter names instead of indices (why?), e.g.
3929 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
3930 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
3931 buf.Append (type.Name);
3933 if (buf.Length == l) {
3934 throw new Exception (string.Format (
3935 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
3936 type.Name, genDeclType, genDeclMethod));
3940 base.AppendTypeName (buf, type);
3942 int numArgs = type.GenericParameters.Count;
3943 if (type.DeclaringType != null)
3944 numArgs -= type.GenericParameters.Count;
3946 buf.Append ('`').Append (numArgs);
3953 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3956 base.AppendGenericType (buf, type);
3958 AppendType (buf, type);
3962 private StringBuilder AppendType (StringBuilder buf, TypeReference type)
3964 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
3965 bool insertNested = false;
3966 int prevParamCount = 0;
3967 foreach (var decl in decls) {
3969 buf.Append (NestedTypeSeparator);
3970 insertNested = true;
3971 base.AppendTypeName (buf, decl);
3972 int argCount = DocUtils.GetGenericArgumentCount (decl);
3973 int numArgs = argCount - prevParamCount;
3974 prevParamCount = argCount;
3976 buf.Append ('`').Append (numArgs);
3981 public override string GetDeclaration (IMemberReference member)
3983 TypeReference r = member as TypeReference;
3985 return "T:" + GetTypeName (r);
3987 return base.GetDeclaration (member);
3990 protected override string GetConstructorName (MethodReference constructor)
3992 return GetMethodDefinitionName (constructor, "#ctor");
3995 protected override string GetMethodName (MethodReference method)
3998 MethodDefinition methodDef = method as MethodDefinition;
3999 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
4002 TypeReference iface;
4003 MethodReference ifaceMethod;
4004 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
4005 AddTypeCount = false;
4006 name = GetTypeName (iface) + "." + ifaceMethod.Name;
4007 AddTypeCount = true;
4009 return GetMethodDefinitionName (method, name);
4012 private string GetMethodDefinitionName (MethodReference method, string name)
4014 StringBuilder buf = new StringBuilder ();
4015 buf.Append (GetTypeName (method.DeclaringType));
4017 buf.Append (name.Replace (".", "#"));
4018 if (method.IsGenericMethod ()) {
4019 GenericParameterCollection genArgs = method.GenericParameters;
4020 if (genArgs.Count > 0)
4021 buf.Append ("``").Append (genArgs.Count);
4023 ParameterDefinitionCollection parameters = method.Parameters;
4025 genDeclType = method.DeclaringType;
4026 genDeclMethod = method;
4027 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
4031 genDeclMethod = null;
4033 return buf.ToString ();
4036 private StringBuilder AppendParameters (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinitionCollection parameters)
4038 if (parameters.Count == 0)
4043 AppendParameter (buf, genArgs, parameters [0]);
4044 for (int i = 1; i < parameters.Count; ++i) {
4046 AppendParameter (buf, genArgs, parameters [i]);
4049 return buf.Append (')');
4052 private StringBuilder AppendParameter (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinition parameter)
4054 AddTypeCount = false;
4055 buf.Append (GetTypeName (parameter.ParameterType));
4056 AddTypeCount = true;
4060 protected override string GetPropertyName (PropertyReference property)
4064 PropertyDefinition propertyDef = property as PropertyDefinition;
4065 MethodDefinition method = null;
4066 if (propertyDef != null)
4067 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
4068 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
4069 name = property.Name;
4071 TypeReference iface;
4072 MethodReference ifaceMethod;
4073 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4074 AddTypeCount = false;
4075 name = string.Join ("#", new string[]{
4076 GetTypeName (iface).Replace (".", "#"),
4077 DocUtils.GetMember (property.Name)
4079 AddTypeCount = true;
4082 StringBuilder buf = new StringBuilder ();
4083 buf.Append (GetName (property.DeclaringType));
4086 ParameterDefinitionCollection parameters = property.Parameters;
4087 if (parameters.Count > 0) {
4088 genDeclType = property.DeclaringType;
4090 GenericParameterCollection genArgs = property.DeclaringType.GenericParameters;
4091 AppendParameter (buf, genArgs, parameters [0]);
4092 for (int i = 1; i < parameters.Count; ++i) {
4094 AppendParameter (buf, genArgs, parameters [i]);
4099 return buf.ToString ();
4102 protected override string GetFieldName (FieldReference field)
4104 return string.Format ("{0}.{1}",
4105 GetName (field.DeclaringType), field.Name);
4108 protected override string GetEventName (EventReference e)
4110 return string.Format ("{0}.{1}",
4111 GetName (e.DeclaringType), e.Name);
4114 protected override string GetTypeDeclaration (TypeDefinition type)
4116 string name = GetName (type);
4122 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4124 string name = GetName (constructor);
4130 protected override string GetMethodDeclaration (MethodDefinition method)
4132 string name = GetName (method);
4135 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4136 genDeclType = method.DeclaringType;
4137 genDeclMethod = method;
4138 name += "~" + GetName (method.ReturnType.ReturnType);
4140 genDeclMethod = null;
4145 protected override string GetPropertyDeclaration (PropertyDefinition property)
4147 string name = GetName (property);
4153 protected override string GetFieldDeclaration (FieldDefinition field)
4155 string name = GetName (field);
4161 protected override string GetEventDeclaration (EventDefinition e)
4163 string name = GetName (e);
4170 class FileNameMemberFormatter : SlashDocMemberFormatter {
4171 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4176 protected override char NestedTypeSeparator {