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",
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 class CrefComparer : XmlNodeComparer {
2025 public CrefComparer ()
2029 public override int Compare (XmlNode x, XmlNode y)
2031 string xType = x.Attributes ["cref"].Value;
2032 string yType = y.Attributes ["cref"].Value;
2033 string xNamespace = GetNamespace (xType);
2034 string yNamespace = GetNamespace (yType);
2036 int c = xNamespace.CompareTo (yNamespace);
2039 return xType.CompareTo (yType);
2042 static string GetNamespace (string type)
2044 int n = type.LastIndexOf ('.');
2046 return type.Substring (0, n);
2047 return string.Empty;
2051 private static void UpdateExceptions (XmlNode docs, IMemberReference member)
2053 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
2054 string cref = slashdocFormatter.GetDeclaration (source.Exception);
2055 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
2058 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
2059 e.SetAttribute ("cref", cref);
2060 e.InnerXml = "To be added; from: <see cref=\"" +
2061 string.Join ("\" />, <see cref=\"",
2062 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
2065 docs.AppendChild (e);
2067 SortXmlNodes (docs, docs.SelectNodes ("exception"),
2068 new CrefComparer ());
2071 private static void NormalizeWhitespace(XmlElement e) {
2072 // Remove all text and whitespace nodes from the element so it
2073 // is outputted with nice indentation and no blank lines.
2074 ArrayList deleteNodes = new ArrayList();
2075 foreach (XmlNode n in e)
2076 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2078 foreach (XmlNode n in deleteNodes)
2079 n.ParentNode.RemoveChild(n);
2082 private static bool UpdateAssemblyVersions (XmlElement root, IMemberReference member, bool add)
2084 TypeDefinition type = member as TypeDefinition;
2086 type = member.DeclaringType as TypeDefinition;
2087 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
2090 private static string GetAssemblyVersion (AssemblyDefinition assembly)
2092 return assembly.Name.Version.ToString();
2095 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
2097 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
2099 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2100 root.AppendChild(e);
2102 MyXmlNodeList matches = new MyXmlNodeList (assemblyVersions.Length);
2103 foreach (XmlElement v in e.SelectNodes ("AssemblyVersion")) {
2104 foreach (string sv in assemblyVersions)
2105 if (v.InnerText == sv)
2108 // matches.Count > 0 && add: ignore -- already present
2109 if (matches.Count > 0 && !add) {
2110 foreach (XmlNode c in matches)
2113 else if (matches.Count == 0 && add) {
2114 foreach (string sv in assemblyVersions) {
2115 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2120 // matches.Count == 0 && !add: ignore -- already not present
2122 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2123 SortXmlNodes (e, avs, new VersionComparer ());
2125 return avs.Count != 0;
2128 // FIXME: get TypeReferences instead of string comparison?
2129 private static string[] IgnorableAttributes = {
2130 // Security related attributes
2131 "System.Reflection.AssemblyKeyFileAttribute",
2132 "System.Reflection.AssemblyDelaySignAttribute",
2133 // Present in @RefType
2134 "System.Runtime.InteropServices.OutAttribute",
2135 // For naming the indexer to use when not using indexers
2136 "System.Reflection.DefaultMemberAttribute",
2137 // for decimal constants
2138 "System.Runtime.CompilerServices.DecimalConstantAttribute",
2139 // compiler generated code
2140 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
2141 // more compiler generated code, e.g. iterator methods
2142 "System.Diagnostics.DebuggerHiddenAttribute",
2143 "System.Runtime.CompilerServices.FixedBufferAttribute",
2144 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
2145 // extension methods
2146 "System.Runtime.CompilerServices.ExtensionAttribute",
2149 private static void MakeAttributes (XmlElement root, CustomAttributeCollection attributes, bool assemblyAttributes)
2151 if (attributes.Count == 0) {
2152 ClearElement(root, "Attributes");
2157 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2161 e = root.OwnerDocument.CreateElement("Attributes");
2163 foreach (CustomAttribute attribute in attributes.Cast<CustomAttribute> ()
2164 .OrderBy (ca => ca.Constructor.DeclaringType.FullName)) {
2165 if (!attribute.Resolve ()) {
2167 Error ("warning: could not resolve type {0}.",
2168 attribute.Constructor.DeclaringType.FullName);
2170 TypeDefinition attrType = attribute.Constructor.DeclaringType as TypeDefinition;
2171 if (attrType != null && !IsPublic (attrType))
2173 if (slashdocFormatter.GetName (attribute.Constructor.DeclaringType) == null)
2176 if (Array.IndexOf (IgnorableAttributes, attribute.Constructor.DeclaringType.FullName) >= 0)
2181 StringList fields = new StringList ();
2183 ParameterDefinitionCollection parameters = attribute.Constructor.Parameters;
2184 for (int i = 0; i < attribute.ConstructorParameters.Count; ++i) {
2185 fields.Add (MakeAttributesValueString (
2186 attribute.ConstructorParameters [i],
2187 parameters [i].ParameterType));
2190 (from de in attribute.Fields.Cast<DictionaryEntry> ()
2191 select new { Type=attribute.GetFieldType (de.Key.ToString ()), Name=de.Key, Value=de.Value })
2193 (from de in attribute.Properties.Cast<DictionaryEntry> ()
2194 select new { Type=attribute.GetPropertyType (de.Key.ToString ()), Name=de.Key, Value=de.Value }))
2195 .OrderBy (v => v.Name);
2196 foreach (var d in namedArgs)
2197 fields.Add (string.Format ("{0}={1}", d.Name,
2198 MakeAttributesValueString (d.Value, d.Type)));
2200 string a2 = String.Join(", ", fields.ToArray ());
2201 if (a2 != "") a2 = "(" + a2 + ")";
2203 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2206 string name = attribute.Constructor.DeclaringType.FullName;
2207 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
2208 WriteElementText(ae, "AttributeName", name + a2);
2211 if (b && e.ParentNode == null)
2212 root.AppendChild(e);
2214 ClearElement(root, "Attributes");
2216 NormalizeWhitespace(e);
2219 private static string MakeAttributesValueString (object v, TypeReference valueType)
2223 if (valueType.FullName == "System.Type")
2224 return "typeof(" + v.ToString () + ")";
2225 if (valueType.FullName == "System.String")
2226 return "\"" + v.ToString () + "\"";
2228 return (bool)v ? "true" : "false";
2229 TypeDefinition valueDef = DocUtils.GetTypeDefinition (valueType);
2230 if (valueDef == null || !valueDef.IsEnum)
2231 return v.ToString ();
2232 string typename = GetDocTypeFullName (valueType);
2233 var values = GetEnumerationValues (valueDef);
2234 ulong c = Convert.ToUInt64 (v);
2235 if (values.ContainsKey (c))
2236 return typename + "." + values [c];
2237 if (valueDef.CustomAttributes.Cast<CustomAttribute> ()
2238 .Any (ca => ca.Constructor.DeclaringType.FullName == "System.FlagsAttribute")) {
2239 return string.Join (" | ",
2240 (from i in values.Keys
2242 select typename + "." + values [i])
2245 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
2248 private static Dictionary<ulong, string> GetEnumerationValues (TypeDefinition type)
2251 (from f in type.Fields.Cast<FieldDefinition> ()
2252 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
2254 .ToDictionary (f => Convert.ToUInt64 (f.Constant), f => f.Name);
2257 private static void MakeParameters (XmlElement root, ParameterDefinitionCollection parameters)
2259 XmlElement e = WriteElement(root, "Parameters");
2261 foreach (ParameterDefinition p in parameters) {
2262 XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
2264 pe.SetAttribute("Name", p.Name);
2265 pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
2266 if (p.ParameterType is ReferenceType) {
2267 if (p.IsOut) pe.SetAttribute("RefType", "out");
2268 else pe.SetAttribute("RefType", "ref");
2270 MakeAttributes (pe, p.CustomAttributes, false);
2274 private static void MakeTypeParameters (XmlElement root, GenericParameterCollection typeParams)
2276 if (typeParams == null || typeParams.Count == 0) {
2277 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2279 root.RemoveChild (f);
2282 XmlElement e = WriteElement(root, "TypeParameters");
2284 foreach (GenericParameter t in typeParams) {
2285 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2287 pe.SetAttribute("Name", t.Name);
2288 MakeAttributes (pe, t.CustomAttributes, false);
2289 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2290 ConstraintCollection constraints = t.Constraints;
2291 GenericParameterAttributes attrs = t.Attributes;
2292 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2300 ce = root.OwnerDocument.CreateElement ("Constraints");
2302 pe.AppendChild (ce);
2303 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2304 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2305 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2306 AppendElementText (ce, "ParameterAttribute", "Covariant");
2307 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2308 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2309 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2310 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2311 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2312 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2313 foreach (TypeReference c in constraints) {
2314 TypeDefinition cd = DocUtils.GetTypeDefinition (c);
2315 AppendElementText (ce,
2316 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2317 GetDocTypeFullName (c));
2322 private static void MakeParameters (XmlElement root, IMemberReference mi)
2324 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2325 MakeParameters (root, ((MethodDefinition)mi).Parameters);
2326 else if (mi is MethodDefinition) {
2327 MethodDefinition mb = (MethodDefinition) mi;
2328 ParameterDefinitionCollection parameters = mb.Parameters;
2329 MakeParameters(root, parameters);
2330 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2331 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2332 p.SetAttribute ("RefType", "this");
2335 else if (mi is PropertyDefinition) {
2336 ParameterDefinitionCollection parameters = ((PropertyDefinition)mi).Parameters;
2337 if (parameters.Count > 0)
2338 MakeParameters(root, parameters);
2342 else if (mi is FieldDefinition) return;
2343 else if (mi is EventDefinition) return;
2344 else throw new ArgumentException();
2347 private static string GetDocParameterType (TypeReference type)
2349 return GetDocTypeFullName (type).Replace ("@", "&");
2352 private static void MakeReturnValue(XmlElement root, TypeReference type, CustomAttributeCollection attributes)
2354 XmlElement e = WriteElement(root, "ReturnValue");
2356 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2357 if (attributes != null)
2358 MakeAttributes(e, attributes, false);
2361 private static void MakeReturnValue (XmlElement root, IMemberReference mi)
2363 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2365 else if (mi is MethodDefinition)
2366 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType.ReturnType, ((MethodDefinition)mi).ReturnType.CustomAttributes);
2367 else if (mi is PropertyDefinition)
2368 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
2369 else if (mi is FieldDefinition)
2370 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
2371 else if (mi is EventDefinition)
2372 MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
2374 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2377 private static XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info) {
2378 IMemberReference mi = info.Member;
2379 if (mi is TypeDefinition) return null;
2381 string sigs = MakeMemberSignature(mi);
2382 if (sigs == null) return null; // not publicly visible
2384 // no documentation for property/event accessors. Is there a better way of doing this?
2385 if (mi.Name.StartsWith("get_")) return null;
2386 if (mi.Name.StartsWith("set_")) return null;
2387 if (mi.Name.StartsWith("add_")) return null;
2388 if (mi.Name.StartsWith("remove_")) return null;
2389 if (mi.Name.StartsWith("raise_")) return null;
2391 XmlElement me = doc.CreateElement("Member");
2392 me.SetAttribute("MemberName", GetMemberName (mi));
2397 if (since != null) {
2398 XmlNode docs = me.SelectSingleNode("Docs");
2399 docs.AppendChild (CreateSinceNode (doc));
2405 private static string GetMemberName (IMemberReference mi)
2407 MethodDefinition mb = mi as MethodDefinition;
2409 PropertyDefinition pi = mi as PropertyDefinition;
2412 return DocUtils.GetPropertyName (pi);
2414 StringBuilder sb = new StringBuilder (mi.Name.Length);
2415 if (!DocUtils.IsExplicitlyImplemented (mb))
2416 sb.Append (mi.Name);
2418 TypeReference iface;
2419 MethodReference ifaceMethod;
2420 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2421 sb.Append (GetDocTypeFullName (iface));
2423 sb.Append (ifaceMethod.Name);
2425 if (mb.IsGenericMethod ()) {
2426 GenericParameterCollection typeParams = mb.GenericParameters;
2427 if (typeParams.Count > 0) {
2429 sb.Append (typeParams [0].Name);
2430 for (int i = 1; i < typeParams.Count; ++i)
2431 sb.Append (",").Append (typeParams [i].Name);
2435 return sb.ToString ();
2438 private static int CountChars (string s, char c)
2441 for (int i = 0; i < s.Length; ++i) {
2448 /// SIGNATURE GENERATION FUNCTIONS
2450 static string MakeTypeSignature (TypeReference type)
2452 return csharpFormatter.GetDeclaration (type);
2455 static string MakeMemberSignature (IMemberReference mi)
2457 return csharpFullFormatter.GetDeclaration (mi);
2460 static string GetMemberType (IMemberReference mi)
2462 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2463 return "Constructor";
2464 if (mi is MethodDefinition)
2466 if (mi is PropertyDefinition)
2468 if (mi is FieldDefinition)
2470 if (mi is EventDefinition)
2472 throw new ArgumentException();
2475 private static string GetDocTypeName (TypeReference type)
2477 return docTypeFormatter.GetName (type);
2480 private static string GetDocTypeFullName (TypeReference type)
2482 return DocTypeFullMemberFormatter.Default.GetName (type);
2485 class DocsNodeInfo {
2486 public DocsNodeInfo (XmlElement node)
2491 public DocsNodeInfo (XmlElement node, TypeDefinition type)
2497 public DocsNodeInfo (XmlElement node, IMemberReference member)
2500 SetMemberInfo (member);
2503 public void SetType (TypeDefinition type)
2506 throw new ArgumentNullException ("type");
2507 GenericParameters = new List<GenericParameter> (type.GenericParameters.Cast<GenericParameter> ());
2508 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
2509 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
2510 for (int i = 0; i < declTypes.Count - 1; ++i) {
2511 int remove = System.Math.Min (maxGenArgs,
2512 DocUtils.GetGenericArgumentCount (declTypes [i]));
2513 maxGenArgs -= remove;
2514 while (remove-- > 0)
2515 GenericParameters.RemoveAt (0);
2517 if (DocUtils.IsDelegate (type)) {
2518 Parameters = type.GetMethod("Invoke").Parameters;
2519 ReturnType = type.GetMethod("Invoke").ReturnType.ReturnType;
2521 SetSlashDocs (type);
2524 public void SetMemberInfo (IMemberReference member)
2527 throw new ArgumentNullException ("member");
2528 ReturnIsReturn = true;
2532 if (member is MethodReference ) {
2533 MethodReference mr = (MethodReference) member;
2534 Parameters = mr.Parameters;
2535 if (mr.IsGenericMethod ()) {
2536 GenericParameters = new List<GenericParameter> (mr.GenericParameters.Cast<GenericParameter> ());
2539 else if (member is PropertyDefinition) {
2540 Parameters = ((PropertyDefinition) member).Parameters;
2543 if (member is MethodDefinition) {
2544 ReturnType = ((MethodDefinition) member).ReturnType.ReturnType;
2545 } else if (member is PropertyDefinition) {
2546 ReturnType = ((PropertyDefinition) member).PropertyType;
2547 ReturnIsReturn = false;
2550 // no remarks section for enum members
2551 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
2553 SetSlashDocs (member);
2556 private void SetSlashDocs (IMemberReference member)
2558 if (slashdocs == null)
2561 string slashdocsig = slashdocFormatter.GetDeclaration (member);
2562 if (slashdocsig != null)
2563 SlashDocs = slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
2566 public TypeReference ReturnType;
2567 public List<GenericParameter> GenericParameters;
2568 public ParameterDefinitionCollection Parameters;
2569 public bool ReturnIsReturn;
2570 public XmlElement Node;
2571 public bool AddRemarks = true;
2572 public XmlNode SlashDocs;
2573 public XmlReader EcmaDocs;
2574 public IMemberReference Member;
2577 static string GetXPathForMember (DocumentationMember member)
2579 StringBuilder xpath = new StringBuilder ();
2580 xpath.Append ("//Members/Member[@MemberName=\"")
2581 .Append (member.MemberName)
2583 if (member.Parameters != null && member.Parameters.Count > 0) {
2584 xpath.Append ("/Parameters[count(Parameter) = ")
2585 .Append (member.Parameters.Count);
2586 for (int i = 0; i < member.Parameters.Count; ++i) {
2587 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2588 xpath.Append (member.Parameters [i]);
2589 xpath.Append ("\"");
2591 xpath.Append ("]/..");
2593 return xpath.ToString ();
2596 public static string GetXPathForMember (XPathNavigator member)
2598 StringBuilder xpath = new StringBuilder ();
2599 xpath.Append ("//Type[@FullName=\"")
2600 .Append (member.SelectSingleNode ("../../@FullName").Value)
2602 xpath.Append ("Members/Member[@MemberName=\"")
2603 .Append (member.SelectSingleNode ("@MemberName").Value)
2605 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2606 if (parameters.Count > 0) {
2607 xpath.Append ("/Parameters[count(Parameter) = ")
2608 .Append (parameters.Count);
2610 while (parameters.MoveNext ()) {
2612 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2613 xpath.Append (parameters.Current.Value);
2614 xpath.Append ("\"");
2616 xpath.Append ("]/..");
2618 return xpath.ToString ();
2621 public static string GetXPathForMember (IMemberReference member)
2623 StringBuilder xpath = new StringBuilder ();
2624 xpath.Append ("//Type[@FullName=\"")
2625 .Append (member.DeclaringType.FullName)
2627 xpath.Append ("Members/Member[@MemberName=\"")
2628 .Append (GetMemberName (member))
2631 ParameterDefinitionCollection parameters = null;
2632 if (member is MethodDefinition)
2633 parameters = ((MethodDefinition) member).Parameters;
2634 else if (member is PropertyDefinition) {
2635 parameters = ((PropertyDefinition) member).Parameters;
2637 if (parameters != null && parameters.Count > 0) {
2638 xpath.Append ("/Parameters[count(Parameter) = ")
2639 .Append (parameters.Count);
2640 for (int i = 0; i < parameters.Count; ++i) {
2641 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2642 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2643 xpath.Append ("\"");
2645 xpath.Append ("]/..");
2647 return xpath.ToString ();
2651 static class CecilExtensions {
2652 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type)
2654 foreach (var c in type.Constructors)
2655 yield return (IMemberReference) c;
2656 foreach (var e in type.Events)
2657 yield return (IMemberReference) e;
2658 foreach (var f in type.Fields)
2659 yield return (IMemberReference) f;
2660 foreach (var m in type.Methods)
2661 yield return (IMemberReference) m;
2662 foreach (var t in type.NestedTypes)
2663 yield return (IMemberReference) t;
2664 foreach (var p in type.Properties)
2665 yield return (IMemberReference) p;
2668 public static IEnumerable<IMemberReference> GetMembers (this TypeDefinition type, string member)
2670 return GetMembers (type).Where (m => m.Name == member);
2673 public static IMemberReference GetMember (this TypeDefinition type, string member)
2675 return GetMembers (type, member).EnsureZeroOrOne ();
2678 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2680 if (source.Count () > 1)
2681 throw new InvalidOperationException ("too many matches");
2682 return source.FirstOrDefault ();
2685 static T EnsureOne<T> (this IEnumerable<T> source)
2687 if (source.Count () > 1)
2688 throw new InvalidOperationException ("too many matches: " +
2689 string.Join ("; ", source.Select (e => e.ToString ()).ToArray ()));
2690 return source.First ();
2693 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2695 return type.Methods.Cast<MethodDefinition> ()
2696 .Where (m => m.Name == method)
2697 .EnsureZeroOrOne ();
2700 public static IEnumerable<IMemberReference> GetDefaultMembers (this TypeReference type)
2702 TypeDefinition def = type as TypeDefinition;
2704 return new IMemberReference [0];
2705 CustomAttribute defMemberAttr = type.CustomAttributes.Cast<CustomAttribute> ()
2706 .Where (c => c.Constructor.DeclaringType.FullName == "System.Reflection.DefaultMemberAttribute")
2708 if (defMemberAttr == null)
2709 return new IMemberReference [0];
2710 string name = (string) defMemberAttr.ConstructorParameters [0];
2711 return def.Properties.Cast<PropertyDefinition> ()
2712 .Where (p => p.Name == name)
2713 .Select (p => (IMemberReference) p);
2716 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2718 return assembly.Modules.Cast<ModuleDefinition> ()
2719 .SelectMany (md => md.Types.Cast<TypeDefinition> ());
2722 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2724 return GetTypes (assembly)
2725 .Where (td => td.FullName == type)
2726 .EnsureZeroOrOne ();
2729 public static bool IsGenericType (this TypeReference type)
2731 return type.GenericParameters.Count > 0;
2734 public static bool IsGenericMethod (this MethodReference method)
2736 return method.GenericParameters.Count > 0;
2739 public static IMemberReference GetDefinition (this IMemberReference member)
2741 EventReference er = member as EventReference;
2743 return GetEventDefinition (er);
2744 FieldReference fr = member as FieldReference;
2746 return GetFieldDefinition (fr);
2747 MethodReference mr = member as MethodReference;
2749 return GetMethodDefinition (mr);
2750 PropertyReference pr = member as PropertyReference;
2752 return GetPropertyDefinition (pr);
2753 TypeReference tr = member as TypeReference;
2755 return GetTypeDefinition (tr);
2756 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2759 public static EventDefinition GetEventDefinition (this EventReference ev)
2761 EventDefinition evDef = ev as EventDefinition;
2764 return (EventDefinition) ev.DeclaringType.GetTypeDefinition ()
2765 .GetMember (ev.Name);
2768 public static FieldDefinition GetFieldDefinition (this FieldReference field)
2770 FieldDefinition fieldDef = field as FieldDefinition;
2771 if (fieldDef != null)
2773 return (FieldDefinition) field.DeclaringType.GetTypeDefinition ()
2774 .GetMember (field.Name);
2777 public static MethodDefinition GetMethodDefinition (this MethodReference method)
2779 MethodDefinition methodDef = method as MethodDefinition;
2780 if (methodDef != null)
2782 method = method.GetOriginalMethod ();
2783 return method.DeclaringType.GetTypeDefinition ()
2784 .GetMembers (method.Name).OfType<MethodDefinition> ()
2786 AreSame (method.ReturnType.ReturnType, m.ReturnType.ReturnType) &&
2787 AreSame (method.Parameters, m.Parameters))
2791 static bool AreSame (ParameterDefinitionCollection a, ParameterDefinitionCollection b)
2793 if (a.Count != b.Count)
2799 for (int i = 0; i < a.Count; i++) {
2800 if (!AreSame (a [i].ParameterType, b [i].ParameterType))
2807 static bool AreSame (TypeReference a, TypeReference b)
2809 while (a is TypeSpecification || b is TypeSpecification) {
2810 if (a.GetType () != b.GetType ())
2813 IGenericInstance ga = a as IGenericInstance;
2814 IGenericInstance gb = b as IGenericInstance;
2816 a = ((TypeSpecification) a).ElementType;
2817 b = ((TypeSpecification) b).ElementType;
2819 if (ga != null && gb != null) {
2820 if (ga.GenericArguments.Count != gb.GenericArguments.Count) {
2823 for (int i = 0; i < ga.GenericArguments.Count; ++i) {
2824 if (!AreSame (ga.GenericArguments [i], gb.GenericArguments [i]))
2830 GenericParameter pa = (a as GenericParameter);
2831 GenericParameter pb = (b as GenericParameter);
2832 if ((pa != null) || (pb != null)) {
2833 if (a.GetType () != b.GetType ())
2836 return pa.Position == pb.Position;
2839 return a.FullName == b.FullName;
2842 public static PropertyDefinition GetPropertyDefinition (this PropertyReference property)
2844 PropertyDefinition propertyDef = property as PropertyDefinition;
2845 if (propertyDef != null)
2847 return (PropertyDefinition) property.DeclaringType.GetTypeDefinition ()
2848 .GetMembers (property.Name).OfType<PropertyDefinition> ()
2849 .Where (p => p.PropertyType.FullName == property.PropertyType.FullName &&
2850 AreSame (property.Parameters, p.Parameters))
2854 public static TypeDefinition GetTypeDefinition (this TypeReference type)
2856 return DocUtils.GetTypeDefinition (type);
2860 static class DocUtils {
2861 public static bool IsExplicitlyImplemented (MethodDefinition method)
2863 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2866 public static string GetTypeDotMember (string name)
2868 int startType, startMethod;
2869 startType = startMethod = -1;
2870 for (int i = 0; i < name.Length; ++i) {
2871 if (name [i] == '.') {
2872 startType = startMethod;
2876 return name.Substring (startType+1);
2879 public static string GetMember (string name)
2881 int i = name.LastIndexOf ('.');
2884 return name.Substring (i+1);
2887 public static void GetInfoForExplicitlyImplementedMethod (
2888 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2892 if (method.Overrides.Count != 1)
2893 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2894 iface = method.Overrides [0].DeclaringType;
2895 ifaceMethod = method.Overrides [0];
2898 public static string GetPropertyName (PropertyDefinition pi)
2900 // Issue: (g)mcs-generated assemblies that explicitly implement
2901 // properties don't specify the full namespace, just the
2902 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2903 MethodDefinition method = pi.GetMethod;
2905 method = pi.SetMethod;
2906 if (!IsExplicitlyImplemented (method))
2909 // Need to determine appropriate namespace for this member.
2910 TypeReference iface;
2911 MethodReference ifaceMethod;
2912 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2913 return string.Join (".", new string[]{
2914 DocTypeFullMemberFormatter.Default.GetName (iface),
2915 GetMember (pi.Name)});
2918 public static string GetNamespace (TypeReference type)
2920 if (type.GetOriginalType ().IsNested)
2921 type = type.GetOriginalType ();
2922 while (type != null && type.IsNested)
2923 type = type.DeclaringType;
2925 return string.Empty;
2926 return type.Namespace;
2929 public static string PathCombine (string dir, string path)
2935 return Path.Combine (dir, path);
2938 public static bool IsExtensionMethod (MethodDefinition method)
2941 method.CustomAttributes.Cast<CustomAttribute> ()
2942 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2944 method.DeclaringType.CustomAttributes.Cast<CustomAttribute> ()
2945 .Where (m => m.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2949 public static bool IsDelegate (TypeDefinition type)
2951 TypeReference baseRef = type.BaseType;
2952 if (baseRef == null)
2954 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2955 baseRef.FullName == "System.MulticastDelegate";
2958 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2960 List<TypeReference> decls = new List<TypeReference> ();
2962 while (type.DeclaringType != null) {
2963 decls.Add (type.DeclaringType);
2964 type = type.DeclaringType;
2970 public static int GetGenericArgumentCount (TypeReference type)
2972 GenericInstanceType inst = type as GenericInstanceType;
2974 ? inst.GenericArguments.Count
2975 : type.GenericParameters.Count;
2978 public static TypeDefinition GetTypeDefinition (TypeReference type)
2980 // Remove generic instantiation info (so string comparison below works)
2981 type = type.GetOriginalType ();
2982 TypeDefinition typeDef = type as TypeDefinition;
2983 if (typeDef != null)
2986 AssemblyNameReference reference = type.Scope as AssemblyNameReference;
2987 if (reference != null) {
2988 AssemblyDefinition ad = type.Module.Assembly.Resolver.Resolve (reference);
2989 if (ad != null && (typeDef = ad.MainModule.Types [type.FullName]) != null)
2992 ModuleDefinition module = type.Scope as ModuleDefinition;
2993 if (module != null && (typeDef = module.Types [type.FullName]) != null)
2998 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
3000 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
3001 List<TypeReference> userInterfaces = new List<TypeReference> ();
3002 foreach (TypeReference iface in type.Interfaces) {
3003 TypeReference lookup = GetTypeDefinition (iface) ?? iface;
3004 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
3005 userInterfaces.Add (iface);
3007 return userInterfaces;
3010 private static string GetQualifiedTypeName (TypeReference type)
3012 return "[" + type.Scope.Name + "]" + type.FullName;
3015 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
3017 HashSet<string> inheritedInterfaces = new HashSet<string> ();
3018 Action<TypeDefinition> a = null;
3020 if (t == null) return;
3021 foreach (TypeReference r in t.Interfaces) {
3022 inheritedInterfaces.Add (GetQualifiedTypeName (r));
3023 a (GetTypeDefinition (r));
3026 TypeReference baseRef = type.BaseType;
3027 while (baseRef != null) {
3028 TypeDefinition baseDef = GetTypeDefinition (baseRef);
3029 if (baseDef != null) {
3031 baseRef = baseDef.BaseType;
3036 foreach (TypeReference r in type.Interfaces)
3037 a (GetTypeDefinition (r));
3038 return inheritedInterfaces;
3042 class DocumentationMember {
3043 public StringToStringMap MemberSignatures = new StringToStringMap ();
3044 public string ReturnType;
3045 public StringList Parameters;
3046 public string MemberName;
3047 public string MemberType;
3049 public DocumentationMember (XmlReader reader)
3051 MemberName = reader.GetAttribute ("MemberName");
3052 int depth = reader.Depth;
3054 StringList p = new StringList ();
3056 if (reader.NodeType != XmlNodeType.Element)
3058 switch (reader.Name) {
3059 case "MemberSignature":
3060 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3063 MemberType = reader.ReadElementString ();
3066 if (reader.Depth == depth + 2)
3067 ReturnType = reader.ReadElementString ();
3070 if (reader.Depth == depth + 2)
3071 p.Add (reader.GetAttribute ("Type"));
3074 if (reader.Depth == depth + 1)
3078 } while (go && reader.Read () && reader.Depth >= depth);
3084 public DocumentationMember (XmlNode node)
3086 MemberName = node.Attributes ["MemberName"].Value;
3087 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3088 XmlAttribute l = n.Attributes ["Language"];
3089 XmlAttribute v = n.Attributes ["Value"];
3090 if (l != null && v != null)
3091 MemberSignatures [l.Value] = v.Value;
3093 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3094 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
3096 ReturnType = rt.InnerText;
3097 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
3099 Parameters = new StringList (p.Count);
3100 for (int i = 0; i < p.Count; ++i)
3101 Parameters.Add (p [i].Attributes ["Type"].Value);
3106 public abstract class MemberFormatter {
3107 public virtual string GetName (IMemberReference member)
3109 TypeReference type = member as TypeReference;
3111 return GetTypeName (type);
3112 MethodReference method = member as MethodReference;
3113 if (method != null && method.Name == ".ctor") // method.IsConstructor
3114 return GetConstructorName (method);
3116 return GetMethodName (method);
3117 PropertyReference prop = member as PropertyReference;
3119 return GetPropertyName (prop);
3120 FieldReference field = member as FieldReference;
3122 return GetFieldName (field);
3123 EventReference e = member as EventReference;
3125 return GetEventName (e);
3126 throw new NotSupportedException ("Can't handle: " +
3127 (member == null ? "null" : member.GetType().ToString()));
3130 protected virtual string GetTypeName (TypeReference type)
3133 throw new ArgumentNullException ("type");
3134 return _AppendTypeName (new StringBuilder (type.Name.Length), type).ToString ();
3137 protected virtual char[] ArrayDelimeters {
3138 get {return new char[]{'[', ']'};}
3141 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type)
3143 if (type is ArrayType) {
3144 TypeSpecification spec = type as TypeSpecification;
3145 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3146 .Append (ArrayDelimeters [0]);
3147 ArrayType array = (ArrayType) type;
3148 int rank = array.Rank;
3150 buf.Append (new string (',', rank-1));
3151 return buf.Append (ArrayDelimeters [1]);
3153 if (type is ReferenceType) {
3154 return AppendRefTypeName (buf, type);
3156 if (type is PointerType) {
3157 return AppendPointerTypeName (buf, type);
3159 AppendNamespace (buf, type);
3160 if (type is GenericParameter) {
3161 return AppendTypeName (buf, type);
3163 GenericInstanceType genInst = type as GenericInstanceType;
3164 if (type.GenericParameters.Count == 0 &&
3165 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3166 return AppendFullTypeName (buf, type);
3168 return AppendGenericType (buf, type);
3171 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3173 string ns = DocUtils.GetNamespace (type);
3174 if (ns != null && ns.Length > 0)
3175 buf.Append (ns).Append ('.');
3179 private StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type)
3181 if (type.DeclaringType != null)
3182 AppendFullTypeName (buf, type.DeclaringType).Append (NestedTypeSeparator);
3183 return AppendTypeName (buf, type);
3186 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3188 return AppendTypeName (buf, type.Name);
3191 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3193 int n = typename.IndexOf ("`");
3195 return buf.Append (typename.Substring (0, n));
3196 return buf.Append (typename);
3199 protected virtual string RefTypeModifier {
3203 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type)
3205 TypeSpecification spec = type as TypeSpecification;
3206 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3207 .Append (RefTypeModifier);
3210 protected virtual string PointerModifier {
3214 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type)
3216 TypeSpecification spec = type as TypeSpecification;
3217 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetOriginalType ())
3218 .Append (PointerModifier);
3221 protected virtual char[] GenericTypeContainer {
3222 get {return new char[]{'<', '>'};}
3225 protected virtual char NestedTypeSeparator {
3229 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3231 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3232 type is GenericInstanceType ? type.GetOriginalType () : type);
3233 List<TypeReference> genArgs = GetGenericArguments (type);
3236 bool insertNested = false;
3237 foreach (var decl in decls) {
3238 TypeReference declDef = DocUtils.GetTypeDefinition (decl) ?? decl;
3240 buf.Append (NestedTypeSeparator);
3242 insertNested = true;
3243 AppendTypeName (buf, declDef);
3244 int ac = DocUtils.GetGenericArgumentCount (declDef);
3248 buf.Append (GenericTypeContainer [0]);
3249 _AppendTypeName (buf, genArgs [argIdx++]);
3250 for (int i = 1; i < c; ++i)
3251 _AppendTypeName (buf.Append (","), genArgs [argIdx++]);
3252 buf.Append (GenericTypeContainer [1]);
3258 private List<TypeReference> GetGenericArguments (TypeReference type)
3260 var args = new List<TypeReference> ();
3261 GenericInstanceType inst = type as GenericInstanceType;
3263 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
3265 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
3269 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3274 protected virtual string GetConstructorName (MethodReference constructor)
3276 return constructor.Name;
3279 protected virtual string GetMethodName (MethodReference method)
3284 protected virtual string GetPropertyName (PropertyReference property)
3286 return property.Name;
3289 protected virtual string GetFieldName (FieldReference field)
3294 protected virtual string GetEventName (EventReference e)
3299 public virtual string GetDeclaration (IMemberReference member)
3302 throw new ArgumentNullException ("member");
3303 TypeDefinition type = member as TypeDefinition;
3305 return GetTypeDeclaration (type);
3306 MethodDefinition method = member as MethodDefinition;
3307 if (method != null && method.IsConstructor)
3308 return GetConstructorDeclaration (method);
3310 return GetMethodDeclaration (method);
3311 PropertyDefinition prop = member as PropertyDefinition;
3313 return GetPropertyDeclaration (prop);
3314 FieldDefinition field = member as FieldDefinition;
3316 return GetFieldDeclaration (field);
3317 EventDefinition e = member as EventDefinition;
3319 return GetEventDeclaration (e);
3320 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3323 protected virtual string GetTypeDeclaration (TypeDefinition type)
3326 throw new ArgumentNullException ("type");
3327 StringBuilder buf = new StringBuilder (type.Name.Length);
3328 _AppendTypeName (buf, type);
3329 AppendGenericTypeConstraints (buf, type);
3330 return buf.ToString ();
3333 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
3335 return GetConstructorName (constructor);
3338 protected virtual string GetMethodDeclaration (MethodDefinition method)
3340 // Special signature for destructors.
3341 if (method.Name == "Finalize" && method.Parameters.Count == 0)
3342 return GetFinalizerName (method);
3344 StringBuilder buf = new StringBuilder ();
3346 AppendVisibility (buf, method);
3347 if (buf.Length == 0 &&
3348 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3351 AppendModifiers (buf, method);
3353 if (buf.Length != 0)
3355 buf.Append (GetName (method.ReturnType.ReturnType)).Append (" ");
3357 AppendMethodName (buf, method);
3358 AppendGenericMethod (buf, method).Append (" ");
3359 AppendParameters (buf, method, method.Parameters);
3360 AppendGenericMethodConstraints (buf, method);
3361 return buf.ToString ();
3364 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3366 return buf.Append (method.Name);
3369 protected virtual string GetFinalizerName (MethodDefinition method)
3374 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3379 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3384 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3389 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3394 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3399 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
3401 return GetPropertyName (property);
3404 protected virtual string GetFieldDeclaration (FieldDefinition field)
3406 return GetFieldName (field);
3409 protected virtual string GetEventDeclaration (EventDefinition e)
3411 return GetEventName (e);
3415 class CSharpFullMemberFormatter : MemberFormatter {
3417 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3419 string ns = DocUtils.GetNamespace (type);
3420 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
3421 buf.Append (ns).Append ('.');
3425 private string GetCSharpType (string t)
3428 case "System.Byte": return "byte";
3429 case "System.SByte": return "sbyte";
3430 case "System.Int16": return "short";
3431 case "System.Int32": return "int";
3432 case "System.Int64": return "long";
3434 case "System.UInt16": return "ushort";
3435 case "System.UInt32": return "uint";
3436 case "System.UInt64": return "ulong";
3438 case "System.Single": return "float";
3439 case "System.Double": return "double";
3440 case "System.Decimal": return "decimal";
3441 case "System.Boolean": return "bool";
3442 case "System.Char": return "char";
3443 case "System.Void": return "void";
3444 case "System.String": return "string";
3445 case "System.Object": return "object";
3450 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3452 if (type is GenericParameter)
3453 return buf.Append (type.Name);
3454 string t = type.FullName;
3455 if (!t.StartsWith ("System.")) {
3456 return base.AppendTypeName (buf, type);
3459 string s = GetCSharpType (t);
3461 return buf.Append (s);
3463 return base.AppendTypeName (buf, type);
3466 protected override string GetTypeDeclaration (TypeDefinition type)
3468 string visibility = GetTypeVisibility (type.Attributes);
3469 if (visibility == null)
3472 StringBuilder buf = new StringBuilder ();
3474 buf.Append (visibility);
3477 MemberFormatter full = new CSharpFullMemberFormatter ();
3479 if (DocUtils.IsDelegate (type)) {
3480 buf.Append("delegate ");
3481 MethodDefinition invoke = type.GetMethod ("Invoke");
3482 buf.Append (full.GetName (invoke.ReturnType.ReturnType)).Append (" ");
3483 buf.Append (GetName (type));
3484 AppendParameters (buf, invoke, invoke.Parameters);
3485 AppendGenericTypeConstraints (buf, type);
3488 return buf.ToString();
3491 if (type.IsAbstract && !type.IsInterface)
3492 buf.Append("abstract ");
3493 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
3494 buf.Append("sealed ");
3495 buf.Replace ("abstract sealed", "static");
3497 buf.Append (GetTypeKind (type));
3499 buf.Append (GetCSharpType (type.FullName) == null
3504 TypeReference basetype = type.BaseType;
3505 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
3508 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
3509 .Select (iface => full.GetName (iface))
3513 if (basetype != null || interface_names.Count > 0)
3516 if (basetype != null) {
3517 buf.Append (full.GetName (basetype));
3518 if (interface_names.Count > 0)
3522 for (int i = 0; i < interface_names.Count; i++){
3525 buf.Append (interface_names [i]);
3527 AppendGenericTypeConstraints (buf, type);
3530 return buf.ToString ();
3533 static string GetTypeKind (TypeDefinition t)
3539 if (t.IsClass || t.FullName == "System.Enum")
3543 throw new ArgumentException(t.FullName);
3546 static string GetTypeVisibility (TypeAttributes ta)
3548 switch (ta & TypeAttributes.VisibilityMask) {
3549 case TypeAttributes.Public:
3550 case TypeAttributes.NestedPublic:
3553 case TypeAttributes.NestedFamily:
3554 case TypeAttributes.NestedFamORAssem:
3562 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3564 if (type.GenericParameters.Count == 0)
3566 return AppendConstraints (buf, type.GenericParameters);
3569 private StringBuilder AppendConstraints (StringBuilder buf, GenericParameterCollection genArgs)
3571 foreach (GenericParameter genArg in genArgs) {
3572 GenericParameterAttributes attrs = genArg.Attributes;
3573 ConstraintCollection constraints = genArg.Constraints;
3574 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
3576 buf.Append (" where ").Append (genArg.Name).Append (" : ");
3577 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
3578 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
3579 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
3582 buf.Append ("class");
3586 buf.Append ("struct");
3589 if (constraints.Count > 0 && !isvt) {
3592 buf.Append (GetTypeName (constraints [0]));
3593 for (int i = 1; i < constraints.Count; ++i)
3594 buf.Append (", ").Append (GetTypeName (constraints [i]));
3596 if (isnew && !isvt) {
3599 buf.Append ("new()");
3605 protected override string GetConstructorDeclaration (MethodDefinition constructor)
3607 StringBuilder buf = new StringBuilder ();
3608 AppendVisibility (buf, constructor);
3609 if (buf.Length == 0)
3613 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
3614 AppendParameters (buf, constructor, constructor.Parameters);
3617 return buf.ToString ();
3620 protected override string GetMethodDeclaration (MethodDefinition method)
3622 string decl = base.GetMethodDeclaration (method);
3628 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3630 if (DocUtils.IsExplicitlyImplemented (method)) {
3631 TypeReference iface;
3632 MethodReference ifaceMethod;
3633 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3634 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3636 .Append (ifaceMethod.Name);
3638 return base.AppendMethodName (buf, method);
3641 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3643 if (method.GenericParameters.Count == 0)
3645 return AppendConstraints (buf, method.GenericParameters);
3648 protected override string RefTypeModifier {
3652 protected override string GetFinalizerName (MethodDefinition method)
3654 return "~" + method.DeclaringType.Name + " ()";
3657 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3661 if (method.IsPublic)
3662 return buf.Append ("public");
3663 if (method.IsFamily || method.IsFamilyOrAssembly)
3664 return buf.Append ("protected");
3668 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3670 string modifiers = String.Empty;
3671 if (method.IsStatic) modifiers += " static";
3672 if (method.IsVirtual && !method.IsAbstract) {
3673 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3674 else modifiers += " override";
3676 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
3677 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
3678 if (method.IsFinal) modifiers += " sealed";
3679 if (modifiers == " virtual sealed") modifiers = "";
3681 return buf.Append (modifiers);
3684 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3686 if (method.IsGenericMethod ()) {
3687 GenericParameterCollection args = method.GenericParameters;
3688 if (args.Count > 0) {
3690 buf.Append (args [0].Name);
3691 for (int i = 1; i < args.Count; ++i)
3692 buf.Append (",").Append (args [i].Name);
3699 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters)
3701 return AppendParameters (buf, method, parameters, '(', ')');
3704 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, ParameterDefinitionCollection parameters, char begin, char end)
3708 if (parameters.Count > 0) {
3709 if (DocUtils.IsExtensionMethod (method))
3710 buf.Append ("this ");
3711 AppendParameter (buf, parameters [0]);
3712 for (int i = 1; i < parameters.Count; ++i) {
3714 AppendParameter (buf, parameters [i]);
3718 return buf.Append (end);
3721 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
3723 if (parameter.ParameterType is ReferenceType) {
3724 if (parameter.IsOut)
3725 buf.Append ("out ");
3727 buf.Append ("ref ");
3729 buf.Append (GetName (parameter.ParameterType)).Append (" ");
3730 return buf.Append (parameter.Name);
3733 protected override string GetPropertyDeclaration (PropertyDefinition property)
3735 MethodDefinition method;
3737 string get_visible = null;
3738 if ((method = property.GetMethod) != null &&
3739 (DocUtils.IsExplicitlyImplemented (method) ||
3740 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3741 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3742 string set_visible = null;
3743 if ((method = property.SetMethod) != null &&
3744 (DocUtils.IsExplicitlyImplemented (method) ||
3745 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
3746 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
3748 if ((set_visible == null) && (get_visible == null))
3752 StringBuilder buf = new StringBuilder ();
3753 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
3754 buf.Append (visibility = get_visible);
3755 else if (set_visible != null && get_visible == null)
3756 buf.Append (visibility = set_visible);
3758 buf.Append (visibility = "public");
3760 // Pick an accessor to use for static/virtual/override/etc. checks.
3761 method = property.SetMethod;
3763 method = property.GetMethod;
3765 string modifiers = String.Empty;
3766 if (method.IsStatic) modifiers += " static";
3767 if (method.IsVirtual && !method.IsAbstract) {
3768 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
3769 modifiers += " virtual";
3771 modifiers += " override";
3773 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
3774 if (method.IsAbstract && !declDef.IsInterface)
3775 modifiers += " abstract";
3777 modifiers += " sealed";
3778 if (modifiers == " virtual sealed")
3780 buf.Append (modifiers).Append (' ');
3782 buf.Append (GetName (property.PropertyType)).Append (' ');
3784 IEnumerable<IMemberReference> defs = property.DeclaringType.GetDefaultMembers ();
3785 string name = property.Name;
3786 foreach (IMemberReference mi in defs) {
3787 if (mi == property) {
3792 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
3794 if (property.Parameters.Count != 0) {
3795 AppendParameters (buf, method, property.Parameters, '[', ']');
3799 if (set_visible != null) {
3800 if (set_visible != visibility)
3801 buf.Append (' ').Append (set_visible);
3802 buf.Append (" set;");
3804 if (get_visible != null) {
3805 if (get_visible != visibility)
3806 buf.Append (' ').Append (get_visible);
3807 buf.Append (" get;");
3811 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
3814 protected override string GetFieldDeclaration (FieldDefinition field)
3816 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
3817 if (declType.IsEnum && field.Name == "value__")
3818 return null; // This member of enums aren't documented.
3820 StringBuilder buf = new StringBuilder ();
3821 AppendFieldVisibility (buf, field);
3822 if (buf.Length == 0)
3825 if (declType.IsEnum)
3828 if (field.IsStatic && !field.IsLiteral)
3829 buf.Append (" static");
3830 if (field.IsInitOnly)
3831 buf.Append (" readonly");
3832 if (field.IsLiteral)
3833 buf.Append (" const");
3835 buf.Append (' ').Append (GetName (field.FieldType)).Append (' ');
3836 buf.Append (field.Name);
3837 AppendFieldValue (buf, field);
3840 return buf.ToString ();
3843 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
3846 return buf.Append ("public");
3847 if (field.IsFamily || field.IsFamilyOrAssembly)
3848 return buf.Append ("protected");
3852 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
3854 // enums have a value__ field, which we ignore
3855 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
3856 field.DeclaringType.IsGenericType ())
3858 if (field.HasConstant && field.IsLiteral) {
3861 val = field.Constant;
3866 buf.Append (" = ").Append ("null");
3867 else if (val is Enum)
3868 buf.Append (" = ").Append (val.ToString ());
3869 else if (val is IFormattable) {
3870 string value = ((IFormattable)val).ToString();
3872 value = "\"" + value + "\"";
3873 buf.Append (" = ").Append (value);
3879 protected override string GetEventDeclaration (EventDefinition e)
3881 StringBuilder buf = new StringBuilder ();
3882 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
3886 AppendModifiers (buf, e.AddMethod);
3888 buf.Append (" event ");
3889 buf.Append (GetName (e.EventType)).Append (' ');
3890 buf.Append (e.Name).Append (';');
3892 return buf.ToString ();
3896 class CSharpMemberFormatter : CSharpFullMemberFormatter {
3897 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3903 class DocTypeFullMemberFormatter : MemberFormatter {
3904 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
3906 protected override char NestedTypeSeparator {
3911 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
3912 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3918 class SlashDocMemberFormatter : MemberFormatter {
3920 protected override char[] GenericTypeContainer {
3921 get {return new char[]{'{', '}'};}
3924 private bool AddTypeCount = true;
3926 private TypeReference genDeclType;
3927 private MethodReference genDeclMethod;
3929 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3931 if (type is GenericParameter) {
3933 if (genDeclType != null) {
3934 GenericParameterCollection genArgs = genDeclType.GenericParameters;
3935 for (int i = 0; i < genArgs.Count; ++i) {
3936 if (genArgs [i].Name == type.Name) {
3937 buf.Append ('`').Append (i);
3942 if (genDeclMethod != null) {
3943 GenericParameterCollection genArgs = null;
3944 if (genDeclMethod.IsGenericMethod ()) {
3945 genArgs = genDeclMethod.GenericParameters;
3946 for (int i = 0; i < genArgs.Count; ++i) {
3947 if (genArgs [i].Name == type.Name) {
3948 buf.Append ("``").Append (i);
3954 if (genDeclType == null && genDeclMethod == null) {
3955 // Probably from within an explicitly implemented interface member,
3956 // where CSC uses parameter names instead of indices (why?), e.g.
3957 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
3958 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
3959 buf.Append (type.Name);
3961 if (buf.Length == l) {
3962 throw new Exception (string.Format (
3963 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
3964 type.Name, genDeclType, genDeclMethod));
3968 base.AppendTypeName (buf, type);
3970 int numArgs = type.GenericParameters.Count;
3971 if (type.DeclaringType != null)
3972 numArgs -= type.GenericParameters.Count;
3974 buf.Append ('`').Append (numArgs);
3981 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3984 base.AppendGenericType (buf, type);
3986 AppendType (buf, type);
3990 private StringBuilder AppendType (StringBuilder buf, TypeReference type)
3992 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
3993 bool insertNested = false;
3994 int prevParamCount = 0;
3995 foreach (var decl in decls) {
3997 buf.Append (NestedTypeSeparator);
3998 insertNested = true;
3999 base.AppendTypeName (buf, decl);
4000 int argCount = DocUtils.GetGenericArgumentCount (decl);
4001 int numArgs = argCount - prevParamCount;
4002 prevParamCount = argCount;
4004 buf.Append ('`').Append (numArgs);
4009 public override string GetDeclaration (IMemberReference member)
4011 TypeReference r = member as TypeReference;
4013 return "T:" + GetTypeName (r);
4015 return base.GetDeclaration (member);
4018 protected override string GetConstructorName (MethodReference constructor)
4020 return GetMethodDefinitionName (constructor, "#ctor");
4023 protected override string GetMethodName (MethodReference method)
4026 MethodDefinition methodDef = method as MethodDefinition;
4027 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
4030 TypeReference iface;
4031 MethodReference ifaceMethod;
4032 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
4033 AddTypeCount = false;
4034 name = GetTypeName (iface) + "." + ifaceMethod.Name;
4035 AddTypeCount = true;
4037 return GetMethodDefinitionName (method, name);
4040 private string GetMethodDefinitionName (MethodReference method, string name)
4042 StringBuilder buf = new StringBuilder ();
4043 buf.Append (GetTypeName (method.DeclaringType));
4045 buf.Append (name.Replace (".", "#"));
4046 if (method.IsGenericMethod ()) {
4047 GenericParameterCollection genArgs = method.GenericParameters;
4048 if (genArgs.Count > 0)
4049 buf.Append ("``").Append (genArgs.Count);
4051 ParameterDefinitionCollection parameters = method.Parameters;
4053 genDeclType = method.DeclaringType;
4054 genDeclMethod = method;
4055 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
4059 genDeclMethod = null;
4061 return buf.ToString ();
4064 private StringBuilder AppendParameters (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinitionCollection parameters)
4066 if (parameters.Count == 0)
4071 AppendParameter (buf, genArgs, parameters [0]);
4072 for (int i = 1; i < parameters.Count; ++i) {
4074 AppendParameter (buf, genArgs, parameters [i]);
4077 return buf.Append (')');
4080 private StringBuilder AppendParameter (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinition parameter)
4082 AddTypeCount = false;
4083 buf.Append (GetTypeName (parameter.ParameterType));
4084 AddTypeCount = true;
4088 protected override string GetPropertyName (PropertyReference property)
4092 PropertyDefinition propertyDef = property as PropertyDefinition;
4093 MethodDefinition method = null;
4094 if (propertyDef != null)
4095 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
4096 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
4097 name = property.Name;
4099 TypeReference iface;
4100 MethodReference ifaceMethod;
4101 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4102 AddTypeCount = false;
4103 name = string.Join ("#", new string[]{
4104 GetTypeName (iface).Replace (".", "#"),
4105 DocUtils.GetMember (property.Name)
4107 AddTypeCount = true;
4110 StringBuilder buf = new StringBuilder ();
4111 buf.Append (GetName (property.DeclaringType));
4114 ParameterDefinitionCollection parameters = property.Parameters;
4115 if (parameters.Count > 0) {
4116 genDeclType = property.DeclaringType;
4118 GenericParameterCollection genArgs = property.DeclaringType.GenericParameters;
4119 AppendParameter (buf, genArgs, parameters [0]);
4120 for (int i = 1; i < parameters.Count; ++i) {
4122 AppendParameter (buf, genArgs, parameters [i]);
4127 return buf.ToString ();
4130 protected override string GetFieldName (FieldReference field)
4132 return string.Format ("{0}.{1}",
4133 GetName (field.DeclaringType), field.Name);
4136 protected override string GetEventName (EventReference e)
4138 return string.Format ("{0}.{1}",
4139 GetName (e.DeclaringType), e.Name);
4142 protected override string GetTypeDeclaration (TypeDefinition type)
4144 string name = GetName (type);
4150 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4152 string name = GetName (constructor);
4158 protected override string GetMethodDeclaration (MethodDefinition method)
4160 string name = GetName (method);
4163 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4164 genDeclType = method.DeclaringType;
4165 genDeclMethod = method;
4166 name += "~" + GetName (method.ReturnType.ReturnType);
4168 genDeclMethod = null;
4173 protected override string GetPropertyDeclaration (PropertyDefinition property)
4175 string name = GetName (property);
4181 protected override string GetFieldDeclaration (FieldDefinition field)
4183 string name = GetName (field);
4189 protected override string GetEventDeclaration (EventDefinition e)
4191 string name = GetName (e);
4198 class FileNameMemberFormatter : SlashDocMemberFormatter {
4199 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4204 protected override char NestedTypeSeparator {