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.Collections.ObjectModel;
9 using System.Diagnostics;
10 using System.Globalization;
15 using System.Xml.XPath;
20 using MyXmlNodeList = System.Collections.Generic.List<System.Xml.XmlNode>;
21 using StringList = System.Collections.Generic.List<string>;
22 using StringToStringMap = System.Collections.Generic.Dictionary<string, string>;
23 using StringToXmlNodeMap = System.Collections.Generic.Dictionary<string, System.Xml.XmlNode>;
25 namespace Mono.Documentation {
26 static class NativeTypeManager {
28 static Dictionary<string, string> toNativeType = new Dictionary<string,string>(){
32 {"System.Int32", "System.nint"},
35 {"System.UInt32", "System.nuint"},
38 {"System.Single", "System.nfloat"},
39 {"SizeF", "CoreGraphics.CGSize"},
40 {"System.Drawing.SizeF", "CoreGraphics.CGSize"},
41 {"PointF", "CoreGraphics.CGPoint"},
42 {"System.Drawing.PointF", "CoreGraphics.CGPoint"},
43 {"RectangleF", "CoreGraphics.CGRect" },
44 {"System.Drawing.RectangleF", "CoreGraphics.CGRect"}
47 static Dictionary<string, string> fromNativeType = new Dictionary<string,string>(){
50 {"System.nint", "System.Int32"},
52 {"System.nuint", "System.UInt32"},
54 {"System.nfloat", "System.Single"},
55 {"CoreGraphics.CGSize", "System.Drawing.SizeF"},
56 {"CoreGraphics.CGPoint", "System.Drawing.PointF"},
57 {"CoreGraphics.CGRect", "System.Drawing.RectangleF"},
58 {"MonoTouch.CoreGraphics.CGSize", "System.Drawing.SizeF"},
59 {"MonoTouch.CoreGraphics.CGPoint", "System.Drawing.PointF"},
60 {"MonoTouch.CoreGraphics.CGRect", "System.Drawing.RectangleF"}
63 public static string ConvertToNativeType(string typename) {
68 string valueToCompare = StripToComparableType (typename, ref isOut, ref isArray);
70 if (toNativeType.TryGetValue (valueToCompare, out nvalue)) {
82 public static string ConvertFromNativeType(string typename) {
87 string valueToCompare = StripToComparableType (typename, ref isOut, ref isArray);
89 if (fromNativeType.TryGetValue (valueToCompare, out nvalue)) {
98 // it wasn't one of the native types ... just return it
102 static string StripToComparableType (string typename, ref bool isOut, ref bool isArray)
104 string valueToCompare = typename;
105 if (typename.EndsWith ("[]")) {
106 valueToCompare = typename.Substring (0, typename.Length - 2);
109 if (typename.EndsWith ("&")) {
110 valueToCompare = typename.Substring (0, typename.Length - 1);
113 if (typename.Contains ("<")) {
114 // TODO: Need to recursively process generic parameters
116 return valueToCompare;
119 public static string GetTranslatedName(TypeReference t) {
120 string typename = t.FullName;
122 bool isInAssembly = MDocUpdater.IsInAssemblies (t.Module.Name);
123 if (isInAssembly && MDocUpdater.HasDroppedNamespace () && !typename.StartsWith ("System")) {
124 string nameWithDropped = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typename);
125 return nameWithDropped;
130 class MDocUpdater : MDocCommand
133 List<AssemblyDefinition> assemblies;
134 readonly DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver();
137 bool show_exceptions;
138 bool no_assembly_versions, ignore_missing_types;
139 ExceptionLocations? exceptions;
141 internal int additions = 0, deletions = 0;
143 List<DocumentationImporter> importers = new List<DocumentationImporter> ();
145 DocumentationEnumerator docEnum;
149 static readonly MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
150 static readonly MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
152 static MemberFormatter[] typeFormatters = new MemberFormatter[]{
153 new CSharpMemberFormatter (),
154 new ILMemberFormatter (),
157 static MemberFormatter[] memberFormatters = new MemberFormatter[]{
158 new CSharpFullMemberFormatter (),
159 new ILFullMemberFormatter (),
162 internal static readonly MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
164 MyXmlNodeList extensionMethods = new MyXmlNodeList ();
166 HashSet<string> forwardedTypes = new HashSet<string> ();
168 public static string droppedNamespace = string.Empty;
169 public static bool HasDroppedNamespace() {
170 return !string.IsNullOrWhiteSpace (droppedNamespace);
172 public string PreserveTag { get; set; }
173 public static MDocUpdater Instance { get; private set; }
174 public static bool SwitchingToMagicTypes { get; private set; }
176 public override void Run (IEnumerable<string> args)
179 show_exceptions = DebugOutput;
180 var types = new List<string> ();
181 var p = new OptionSet () {
183 "Delete removed members from the XML files.",
184 v => delete = v != null },
186 "Document potential exceptions that members can generate. {SOURCES} " +
187 "is a comma-separated list of:\n" +
188 " asm Method calls in same assembly\n" +
189 " depasm Method calls in dependent assemblies\n" +
190 " all Record all possible exceptions\n" +
191 " added Modifier; only create <exception/>s\n" +
192 " for NEW types/members\n" +
193 "If nothing is specified, then only exceptions from the member will " +
195 v => exceptions = ParseExceptionLocations (v) },
197 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
200 case "ignore-missing-types":
201 ignore_missing_types = true;
203 case "no-assembly-versions":
204 no_assembly_versions = true;
207 throw new Exception ("Unsupported flag `" + v + "'.");
210 { "fignore-missing-types",
211 "Do not report an error if a --type=TYPE type\nwas not found.",
212 v => ignore_missing_types = v != null },
213 { "fno-assembly-versions",
214 "Do not generate //AssemblyVersion elements.",
215 v => no_assembly_versions = v != null },
217 "Import documentation from {FILE}.",
218 v => AddImporter (v) },
220 "Check for assembly references in {DIRECTORY}.",
221 v => assemblyResolver.AddSearchDirectory (v) },
223 "Ignored for compatibility with update-ecma-xml.",
226 "Root {DIRECTORY} to generate/update documentation.",
229 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
230 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
231 v => assemblyResolver.AddSearchDirectory (Path.GetDirectoryName (v)) },
233 "Manually specify the assembly {VERSION} that new members were added in.",
236 "Only update documentation for {TYPE}.",
237 v => types.Add (v) },
239 "Instructs the update process that {NAMESPACE} has been dropped, so that types and members will match existing documentation nodes.",
240 v => droppedNamespace = v },
242 "If the new assembly is switching to 'magic types', then this switch should be defined.",
243 v => SwitchingToMagicTypes = true },
245 "Do not delete members that don't exist in the assembly, but rather mark them as preserved.",
246 v => PreserveTag = "true" },
248 var assemblies = Parse (p, args, "update",
249 "[OPTIONS]+ ASSEMBLIES",
250 "Create or update documentation from ASSEMBLIES.");
251 if (assemblies == null)
253 if (assemblies.Count == 0)
254 Error ("No assemblies specified.");
256 foreach (var dir in assemblies
257 .Where (a => a.Contains (Path.DirectorySeparatorChar))
258 .Select (a => Path.GetDirectoryName (a)))
259 assemblyResolver.AddSearchDirectory (dir);
261 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
264 throw new InvalidOperationException("The --out option is required.");
266 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
268 // Store types that have been forwarded to avoid duplicate generation
269 GatherForwardedTypes ();
271 docEnum = docEnum ?? new DocumentationEnumerator ();
273 // PERFORM THE UPDATES
275 if (types.Count > 0) {
277 DoUpdateTypes (srcPath, types, srcPath);
280 else if (opts.@namespace != null)
281 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
282 Path.Combine (dest_dir, opts.@namespace));
285 DoUpdateAssemblies (srcPath, srcPath);
287 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
289 public static bool IsInAssemblies(string name) {
290 var query = Instance.assemblies.Where (a => a.MainModule.Name == name).ToArray ();
291 return query.Length > 0;
293 void AddImporter (string path)
296 XmlReader r = new XmlTextReader (path);
298 while (r.NodeType != XmlNodeType.Element) {
300 Error ("Unable to read XML file: {0}.", path);
302 if (r.LocalName == "doc") {
303 importers.Add (new MsxdocDocumentationImporter (path));
305 else if (r.LocalName == "Libraries") {
306 var ecmadocs = new XmlTextReader (path);
307 docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
308 importers.Add (new EcmaDocumentationImporter (ecmadocs));
311 Error ("Unsupported XML format within {0}.", path);
314 } catch (Exception e) {
315 Environment.ExitCode = 1;
316 Error ("Could not load XML file: {0}.", e.Message);
320 void GatherForwardedTypes ()
322 foreach (var asm in assemblies)
323 foreach (var type in asm.MainModule.ExportedTypes.Where (t => t.IsForwarder).Select (t => t.FullName))
324 forwardedTypes.Add (type);
327 static ExceptionLocations ParseExceptionLocations (string s)
329 ExceptionLocations loc = ExceptionLocations.Member;
332 foreach (var type in s.Split (',')) {
334 case "added": loc |= ExceptionLocations.AddedMembers; break;
335 case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
336 case "asm": loc |= ExceptionLocations.Assembly; break;
337 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
338 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
344 internal void Warning (string format, params object[] args)
346 Message (TraceLevel.Warning, "mdoc: " + format, args);
349 private AssemblyDefinition LoadAssembly (string name)
351 AssemblyDefinition assembly = null;
353 assembly = AssemblyDefinition.ReadAssembly (name, new ReaderParameters { AssemblyResolver = assemblyResolver });
354 } catch (System.IO.FileNotFoundException) { }
356 if (assembly == null)
357 throw new InvalidOperationException("Assembly " + name + " not found.");
362 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
363 OrderTypeAttributes (element);
364 XmlTextWriter writer = new XmlTextWriter(output);
365 writer.Formatting = Formatting.Indented;
366 writer.Indentation = 2;
367 writer.IndentChar = ' ';
368 element.WriteTo(writer);
372 private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
374 Action<string> creator = file => {
375 using (var writer = OpenWrite (file, mode))
379 MdocFile.UpdateFile (filename, creator);
382 private static void OrderTypeAttributes (XmlElement e)
384 foreach (XmlElement type in e.SelectNodes ("//Type")) {
385 OrderTypeAttributes (type.Attributes);
389 static readonly string[] TypeAttributeOrder = {
390 "Name", "FullName", "FullNameSP", "Maintainer"
393 private static void OrderTypeAttributes (XmlAttributeCollection c)
395 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
396 for (int i = 0; i < c.Count; ++i) {
397 XmlAttribute a = c [i];
398 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
399 if (a.Name == TypeAttributeOrder [j]) {
405 for (int i = attrs.Length-1; i >= 0; --i) {
406 XmlAttribute n = attrs [i];
409 XmlAttribute r = null;
410 for (int j = i+1; j < attrs.Length; ++j) {
411 if (attrs [j] != null) {
419 c.InsertBefore (n, r);
423 private XmlDocument CreateIndexStub()
425 XmlDocument index = new XmlDocument();
427 XmlElement index_root = index.CreateElement("Overview");
428 index.AppendChild(index_root);
430 if (assemblies.Count == 0)
431 throw new Exception ("No assembly");
433 XmlElement index_assemblies = index.CreateElement("Assemblies");
434 index_root.AppendChild(index_assemblies);
436 XmlElement index_remarks = index.CreateElement("Remarks");
437 index_remarks.InnerText = "To be added.";
438 index_root.AppendChild(index_remarks);
440 XmlElement index_copyright = index.CreateElement("Copyright");
441 index_copyright.InnerText = "To be added.";
442 index_root.AppendChild(index_copyright);
444 XmlElement index_types = index.CreateElement("Types");
445 index_root.AppendChild(index_types);
450 private static void WriteNamespaceStub(string ns, string outdir) {
451 XmlDocument index = new XmlDocument();
453 XmlElement index_root = index.CreateElement("Namespace");
454 index.AppendChild(index_root);
456 index_root.SetAttribute("Name", ns);
458 XmlElement index_docs = index.CreateElement("Docs");
459 index_root.AppendChild(index_docs);
461 XmlElement index_summary = index.CreateElement("summary");
462 index_summary.InnerText = "To be added.";
463 index_docs.AppendChild(index_summary);
465 XmlElement index_remarks = index.CreateElement("remarks");
466 index_remarks.InnerText = "To be added.";
467 index_docs.AppendChild(index_remarks);
469 WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
470 writer => WriteXml (index.DocumentElement, writer));
473 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
475 var index = CreateIndexForTypes (dest);
477 var found = new HashSet<string> ();
478 foreach (AssemblyDefinition assembly in assemblies) {
479 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames)) {
480 string relpath = DoUpdateType (type, basepath, dest);
484 found.Add (type.FullName);
489 index.Add (assembly);
497 if (ignore_missing_types)
500 var notFound = from n in typenames where !found.Contains (n) select n;
502 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
505 class IndexForTypes {
511 XmlElement index_types;
512 XmlElement index_assemblies;
514 public IndexForTypes (MDocUpdater app, string indexFile, XmlDocument index)
517 this.indexFile = indexFile;
520 index_types = WriteElement (index.DocumentElement, "Types");
521 index_assemblies = WriteElement (index.DocumentElement, "Assemblies");
524 public void Add (AssemblyDefinition assembly)
526 if (index_assemblies.SelectSingleNode ("Assembly[@Name='" + assembly.Name.Name + "']") != null)
529 app.AddIndexAssembly (assembly, index_assemblies);
532 public void Add (TypeDefinition type)
534 app.AddIndexType (type, index_types);
539 SortIndexEntries (index_types);
540 WriteFile (indexFile, FileMode.Create,
541 writer => WriteXml (index.DocumentElement, writer));
545 IndexForTypes CreateIndexForTypes (string dest)
547 string indexFile = Path.Combine (dest, "index.xml");
548 if (File.Exists (indexFile))
550 return new IndexForTypes (this, indexFile, CreateIndexStub ());
553 /// <summary>Constructs the presumed path to the type's documentation file</summary>
554 /// <returns><c>true</c>, if the type file was found, <c>false</c> otherwise.</returns>
555 /// <param name="result">A typle that contains 1) the 'reltypefile', 2) the 'typefile', and 3) the file info</param>
556 bool TryFindTypeFile(string nsname, string typename, string basepath, out Tuple<string, string, FileInfo> result) {
557 string reltypefile = DocUtils.PathCombine (nsname, typename + ".xml");
558 string typefile = Path.Combine (basepath, reltypefile);
559 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
561 result = new Tuple<string, string, FileInfo> (reltypefile, typefile, file);
566 public string DoUpdateType (TypeDefinition type, string basepath, string dest)
568 if (type.Namespace == null)
569 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
571 if (!IsPublic (type))
574 // Must get the A+B form of the type name.
575 string typename = GetTypeFileName(type);
576 string nsname = DocUtils.GetNamespace (type);
578 // Find the file, if it exists
579 string[] searchLocations = new string[] {
583 if (MDocUpdater.HasDroppedNamespace ()) {
584 // If dropping namespace, types may have moved into a couple of different places.
585 var newSearchLocations = searchLocations.Union (new string[] {
586 string.Format ("{0}.{1}", droppedNamespace, nsname),
587 nsname.Replace (droppedNamespace + ".", string.Empty),
588 MDocUpdater.droppedNamespace
591 searchLocations = newSearchLocations.ToArray ();
594 string reltypefile="", typefile="";
595 System.IO.FileInfo file = null;
597 foreach (var f in searchLocations) {
598 Tuple<string, string, FileInfo> result;
599 bool fileExists = TryFindTypeFile (f, typename, basepath, out result);
602 reltypefile = result.Item1;
603 typefile = result.Item2;
610 if (file == null || !file.Exists) {
611 // we were not able to find a file, let's use the original type informatio.
612 // so that we create the stub in the right place.
613 Tuple<string, string, FileInfo> result;
614 TryFindTypeFile (nsname, typename, basepath, out result);
616 reltypefile = result.Item1;
617 typefile = result.Item2;
621 string output = null;
624 } else if (dest == "-") {
627 output = Path.Combine (dest, reltypefile);
630 if (file != null && file.Exists) {
632 XmlDocument basefile = new XmlDocument();
634 basefile.Load(typefile);
635 } catch (Exception e) {
636 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
639 DoUpdateType2("Updating", basefile, type, output, false);
642 XmlElement td = StubType(type, output);
646 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
649 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
655 public void DoUpdateNS (string ns, string nspath, string outpath)
657 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
658 AssemblyDefinition assembly = assemblies [0];
660 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
661 XmlDocument basefile = new XmlDocument();
662 string typefile = Path.Combine(nspath, file.Name);
664 basefile.Load(typefile);
665 } catch (Exception e) {
666 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
670 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
671 TypeDefinition type = assembly.GetType(typename);
674 if (!string.IsNullOrWhiteSpace (droppedNamespace)) {
675 string nameWithNs = string.Format ("{0}.{1}", droppedNamespace, typename);
676 type = assembly.GetType (nameWithNs);
678 Warning ("Type no longer in assembly: " + typename);
685 seenTypes[type] = seenTypes;
686 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false);
689 // Stub types not in the directory
690 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
691 if (type.Namespace != ns || seenTypes.ContainsKey(type))
694 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"));
695 if (td == null) continue;
699 private static string GetTypeFileName (TypeReference type)
701 return filenameFormatter.GetName (type);
704 public static string GetTypeFileName (string typename)
706 StringBuilder filename = new StringBuilder (typename.Length);
710 for (int i = 0; i < typename.Length; ++i) {
711 char c = typename [i];
720 filename.Append ('`').Append ((numArgs+1).ToString());
735 return filename.ToString ();
738 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
740 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
741 index_assembly.SetAttribute ("Name", assembly.Name.Name);
742 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
744 AssemblyNameDefinition name = assembly.Name;
745 if (name.HasPublicKey) {
746 XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
747 var key = new StringBuilder (name.PublicKey.Length*3 + 2);
749 foreach (byte b in name.PublicKey)
750 key.AppendFormat ("{0,2:x2} ", b);
752 pubkey.InnerText = key.ToString ();
753 index_assembly.AppendChild (pubkey);
756 if (!string.IsNullOrEmpty (name.Culture)) {
757 XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
758 culture.InnerText = name.Culture;
759 index_assembly.AppendChild (culture);
762 MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
763 parent.AppendChild(index_assembly);
766 private void AddIndexType (TypeDefinition type, XmlElement index_types)
768 string typename = GetTypeFileName(type);
770 // Add namespace and type nodes into the index file as needed
771 string ns = DocUtils.GetNamespace (type);
772 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode ("Namespace[@Name='" + ns + "']");
773 if (nsnode == null) {
774 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
775 nsnode.SetAttribute ("Name", ns);
776 index_types.AppendChild (nsnode);
778 string doc_typename = GetDocTypeName (type);
779 XmlElement typenode = (XmlElement) nsnode.SelectSingleNode ("Type[@Name='" + typename + "']");
780 if (typenode == null) {
781 typenode = index_types.OwnerDocument.CreateElement ("Type");
782 typenode.SetAttribute ("Name", typename);
783 nsnode.AppendChild (typenode);
785 if (typename != doc_typename)
786 typenode.SetAttribute("DisplayName", doc_typename);
788 typenode.RemoveAttribute("DisplayName");
790 typenode.SetAttribute ("Kind", GetTypeKind (type));
793 private void DoUpdateAssemblies (string source, string dest)
795 string indexfile = dest + "/index.xml";
797 if (System.IO.File.Exists(indexfile)) {
798 index = new XmlDocument();
799 index.Load(indexfile);
802 ClearElement(index.DocumentElement, "Assembly");
803 ClearElement(index.DocumentElement, "Attributes");
805 index = CreateIndexStub();
808 string defaultTitle = "Untitled";
809 if (assemblies.Count == 1)
810 defaultTitle = assemblies[0].Name.Name;
811 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
813 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
814 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
815 index_assemblies.RemoveAll ();
818 HashSet<string> goodfiles = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
820 foreach (AssemblyDefinition assm in assemblies) {
821 AddIndexAssembly (assm, index_assemblies);
822 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
825 SortIndexEntries (index_types);
827 CleanupFiles (dest, goodfiles);
828 CleanupIndexTypes (index_types, goodfiles);
829 CleanupExtensions (index_types);
831 WriteFile (indexfile, FileMode.Create,
832 writer => WriteXml(index.DocumentElement, writer));
835 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
837 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
839 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
840 string typename = GetTypeFileName(type);
841 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0 || forwardedTypes.Contains (type.FullName))
844 string reltypepath = DoUpdateType (type, source, dest);
845 if (reltypepath == null)
848 // Add namespace and type nodes into the index file as needed
849 AddIndexType (type, index_types);
851 // Ensure the namespace index file exists
852 string namespaceToUse = type.Namespace;
853 if (HasDroppedNamespace()) {
854 namespaceToUse = string.Format ("{0}.{1}", droppedNamespace, namespaceToUse);
856 string onsdoc = DocUtils.PathCombine (dest, namespaceToUse + ".xml");
857 string nsdoc = DocUtils.PathCombine (dest, "ns-" + namespaceToUse + ".xml");
858 if (File.Exists (onsdoc)) {
859 File.Move (onsdoc, nsdoc);
862 if (!File.Exists (nsdoc)) {
863 Console.WriteLine("New Namespace File: " + type.Namespace);
864 WriteNamespaceStub(namespaceToUse, dest);
867 goodfiles.Add (reltypepath);
871 private static void SortIndexEntries (XmlElement indexTypes)
873 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
874 XmlNodeComparer c = new AttributeNameComparer ();
875 SortXmlNodes (indexTypes, namespaces, c);
877 for (int i = 0; i < namespaces.Count; ++i)
878 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
881 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
883 MyXmlNodeList l = new MyXmlNodeList (children.Count);
884 for (int i = 0; i < children.Count; ++i)
885 l.Add (children [i]);
887 for (int i = l.Count - 1; i > 0; --i) {
888 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
892 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
894 public abstract int Compare (XmlNode x, XmlNode y);
896 public int Compare (object x, object y)
898 return Compare ((XmlNode) x, (XmlNode) y);
902 class AttributeNameComparer : XmlNodeComparer {
905 public AttributeNameComparer ()
910 public AttributeNameComparer (string attribute)
912 this.attribute = attribute;
915 public override int Compare (XmlNode x, XmlNode y)
917 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
921 class VersionComparer : XmlNodeComparer {
922 public override int Compare (XmlNode x, XmlNode y)
924 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
925 string a = GetVersion (x.InnerText);
926 string b = GetVersion (y.InnerText);
927 return new Version (a).CompareTo (new Version (b));
930 static string GetVersion (string v)
932 int n = v.IndexOf ("x");
935 return v.Substring (0, n-1);
939 private static string GetTypeKind (TypeDefinition type)
942 return "Enumeration";
943 if (type.IsValueType)
945 if (type.IsInterface)
947 if (DocUtils.IsDelegate (type))
949 if (type.IsClass || type.FullName == "System.Enum") // FIXME
951 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
954 private static bool IsPublic (TypeDefinition type)
956 TypeDefinition decl = type;
957 while (decl != null) {
958 if (!(decl.IsPublic || decl.IsNestedPublic ||
959 decl.IsNestedFamily || decl.IsNestedFamily || decl.IsNestedFamilyOrAssembly)) {
962 decl = (TypeDefinition) decl.DeclaringType;
967 private void CleanupFiles (string dest, HashSet<string> goodfiles)
969 // Look for files that no longer correspond to types
970 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
971 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
972 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
973 if (!goodfiles.Contains (relTypeFile)) {
974 XmlDocument doc = new XmlDocument ();
975 doc.Load (typefile.FullName);
976 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
977 if (e != null && !no_assembly_versions && UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
978 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
979 WriteXml(doc.DocumentElement, writer);
980 goodfiles.Add (relTypeFile);
984 if (string.IsNullOrWhiteSpace (PreserveTag)) { // only do this if there was no -preserve
985 string newname = typefile.FullName + ".remove";
986 try { System.IO.File.Delete(newname); } catch (Exception) { }
987 try { typefile.MoveTo(newname); } catch (Exception) { }
988 Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
995 private static TextWriter OpenWrite (string path, FileMode mode)
997 var w = new StreamWriter (
998 new FileStream (path, mode),
999 new UTF8Encoding (false)
1005 private string[] GetAssemblyVersions ()
1007 return (from a in assemblies select GetAssemblyVersion (a)).ToArray ();
1010 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
1012 // Look for type nodes that no longer correspond to types
1013 MyXmlNodeList remove = new MyXmlNodeList ();
1014 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
1015 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
1016 if (!goodfiles.Contains (fulltypename)) {
1017 remove.Add (typenode);
1020 foreach (XmlNode n in remove)
1021 n.ParentNode.RemoveChild (n);
1024 private void CleanupExtensions (XmlElement index_types)
1026 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
1027 if (extensionMethods.Count == 0) {
1030 index_types.SelectSingleNode ("/Overview").RemoveChild (e);
1034 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
1035 index_types.SelectSingleNode ("/Overview").AppendChild (e);
1039 extensionMethods.Sort (DefaultExtensionMethodComparer);
1040 foreach (XmlNode m in extensionMethods) {
1041 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
1045 class ExtensionMethodComparer : XmlNodeComparer {
1046 public override int Compare (XmlNode x, XmlNode y)
1048 XmlNode xLink = x.SelectSingleNode ("Member/Link");
1049 XmlNode yLink = y.SelectSingleNode ("Member/Link");
1051 int n = xLink.Attributes ["Type"].Value.CompareTo (
1052 yLink.Attributes ["Type"].Value);
1055 n = xLink.Attributes ["Member"].Value.CompareTo (
1056 yLink.Attributes ["Member"].Value);
1057 if (n == 0 && !object.ReferenceEquals (x, y))
1058 throw new InvalidOperationException ("Duplicate extension method found!");
1063 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
1065 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince)
1067 Console.WriteLine(message + ": " + type.FullName);
1069 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
1071 // Update type metadata
1072 UpdateType(basefile.DocumentElement, type);
1074 // Update existing members. Delete member nodes that no longer should be there,
1075 // and remember what members are already documented so we don't add them again.
1077 MyXmlNodeList todelete = new MyXmlNodeList ();
1079 foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
1080 XmlElement oldmember = info.Node;
1081 MemberReference oldmember2 = info.Member;
1082 string sig = oldmember2 != null ? memberFormatters [0].GetDeclaration (oldmember2) : null;
1084 // Interface implementations and overrides are deleted from the docs
1085 // unless the overrides option is given.
1086 if (oldmember2 != null && sig == null)
1089 // Deleted (or signature changed)
1090 if (oldmember2 == null) {
1091 if (!no_assembly_versions && UpdateAssemblyVersions (oldmember, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
1094 DeleteMember ("Member Removed", output, oldmember, todelete);
1099 if (seenmembers.ContainsKey (sig)) {
1100 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
1101 // ignore, already seen
1103 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
1104 DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
1106 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
1110 // Update signature information
1113 // get all apistyles of sig from info.Node
1114 var styles = oldmember.GetElementsByTagName ("MemberSignature").Cast<XmlElement> ()
1115 .Where (x => x.GetAttribute ("Language") == "C#" && !seenmembers.ContainsKey(x.GetAttribute("Value")))
1116 .Select (x => x.GetAttribute ("Value"));
1118 foreach (var stylesig in styles) {
1119 seenmembers.Add (stylesig, oldmember);
1122 foreach (XmlElement oldmember in todelete)
1123 oldmember.ParentNode.RemoveChild (oldmember);
1126 if (!DocUtils.IsDelegate (type)) {
1127 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
1128 var typemembers = type.GetMembers()
1130 if (m is TypeDefinition) return false;
1131 string sig = memberFormatters [0].GetDeclaration (m);
1132 if (sig == null) return false;
1133 if (seenmembers.ContainsKey(sig)) return false;
1137 foreach (MemberReference m in typemembers) {
1138 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
1139 if (mm == null) continue;
1141 if (MDocUpdater.SwitchingToMagicTypes) {
1142 // this is a unified style API that obviously doesn't exist in the classic API. Let's mark
1143 // it with apistyle="unified", so that it's not displayed for classic style APIs
1144 mm.SetAttribute ("apistyle", "unified");
1147 members.AppendChild( mm );
1149 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
1154 // Import code snippets from files
1155 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
1156 if (!(code is XmlElement)) continue;
1157 string file = ((XmlElement)code).GetAttribute("src");
1158 string lang = ((XmlElement)code).GetAttribute("lang");
1160 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
1162 code.InnerText = src;
1166 if (insertSince && since != null) {
1167 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
1168 docs.AppendChild (CreateSinceNode (basefile));
1172 XmlElement d = basefile.DocumentElement ["Docs"];
1173 XmlElement m = basefile.DocumentElement ["Members"];
1174 if (d != null && m != null)
1175 basefile.DocumentElement.InsertBefore (
1176 basefile.DocumentElement.RemoveChild (d), m);
1177 SortTypeMembers (m);
1181 WriteXml(basefile.DocumentElement, Console.Out);
1183 FileInfo file = new FileInfo (output);
1184 if (!file.Directory.Exists) {
1185 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
1186 file.Directory.Create ();
1188 WriteFile (output, FileMode.Create,
1189 writer => WriteXml(basefile.DocumentElement, writer));
1193 private string GetCodeSource (string lang, string file)
1196 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
1197 // Grab the specified region
1198 string region = "#region " + file.Substring (anchorStart + 4);
1199 file = file.Substring (0, anchorStart + 3);
1201 using (StreamReader reader = new StreamReader (file)) {
1203 StringBuilder src = new StringBuilder ();
1205 while ((line = reader.ReadLine ()) != null) {
1206 if (line.Trim() == region) {
1207 indent = line.IndexOf (region);
1210 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
1215 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
1218 return src.ToString ();
1220 } catch (Exception e) {
1221 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
1222 file, region, show_exceptions ? e.ToString () : e.Message);
1227 using (StreamReader reader = new StreamReader (file))
1228 return reader.ReadToEnd ();
1229 } catch (Exception e) {
1230 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
1235 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
1237 string format = output != null
1238 ? "{0}: File='{1}'; Signature='{4}'"
1239 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1243 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1244 member.Attributes ["MemberName"].Value,
1245 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
1246 if (!delete && MemberDocsHaveUserContent (member)) {
1247 Warning ("Member deletions must be enabled with the --delete option.");
1248 } else if (HasDroppedNamespace ()) {
1249 // if we're dropping the namespace, add the "classic style"
1250 var existingAttribute = member.Attributes ["apistyle"];
1251 if (existingAttribute != null) {
1252 existingAttribute.Value = "classic";
1254 // add the attribute and do not remove
1255 XmlAttribute apistyleAttr = member.OwnerDocument.CreateAttribute ("apistyle");
1257 apistyleAttr.Value = "classic";
1259 member.Attributes.Append (apistyleAttr);
1261 } else if (!HasDroppedNamespace () && member.Attributes ["apistyle"] != null && member.Attributes ["apistyle"].Value == "unified") {
1262 // do nothing if there's an apistyle=new attribute and we haven't dropped the namespace
1263 } else if (!string.IsNullOrWhiteSpace (PreserveTag)) {
1266 todelete.Add (member);
1271 class MemberComparer : XmlNodeComparer {
1272 public override int Compare (XmlNode x, XmlNode y)
1275 string xMemberName = x.Attributes ["MemberName"].Value;
1276 string yMemberName = y.Attributes ["MemberName"].Value;
1278 // generic methods *end* with '>'
1279 // it's possible for explicitly implemented generic interfaces to
1280 // contain <...> without being a generic method
1281 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1282 (r = xMemberName.CompareTo (yMemberName)) != 0)
1286 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1287 xMemberName = xMemberName.Substring (0, lt);
1288 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1289 yMemberName = yMemberName.Substring (0, lt);
1290 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1293 // if @MemberName matches, then it's either two different types of
1294 // members sharing the same name, e.g. field & property, or it's an
1295 // overloaded method.
1296 // for different type, sort based on MemberType value.
1297 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1298 y.SelectSingleNode ("MemberType").InnerText);
1302 // same type -- must be an overloaded method. Sort based on type
1303 // parameter count, then parameter count, then by the parameter
1305 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1306 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1307 if (xTypeParams.Count != yTypeParams.Count)
1308 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1309 for (int i = 0; i < xTypeParams.Count; ++i) {
1310 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1311 yTypeParams [i].Attributes ["Name"].Value);
1316 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1317 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1318 if (xParams.Count != yParams.Count)
1319 return xParams.Count <= yParams.Count ? -1 : 1;
1320 for (int i = 0; i < xParams.Count; ++i) {
1321 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1322 yParams [i].Attributes ["Type"].Value);
1326 // all parameters match, but return value might not match if it was
1327 // changed between one version and another.
1328 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1329 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1330 if (xReturn != null && yReturn != null) {
1331 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1340 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1342 private static void SortTypeMembers (XmlNode members)
1344 if (members == null)
1346 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1349 private static bool MemberDocsHaveUserContent (XmlNode e)
1351 e = (XmlElement)e.SelectSingleNode("Docs");
1352 if (e == null) return false;
1353 foreach (XmlElement d in e.SelectNodes("*"))
1354 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1359 // UPDATE HELPER FUNCTIONS
1361 // CREATE A STUB DOCUMENTATION FILE
1363 public XmlElement StubType (TypeDefinition type, string output)
1365 string typesig = typeFormatters [0].GetDeclaration (type);
1366 if (typesig == null) return null; // not publicly visible
1368 XmlDocument doc = new XmlDocument();
1369 XmlElement root = doc.CreateElement("Type");
1370 doc.AppendChild (root);
1372 DoUpdateType2 ("New Type", doc, type, output, true);
1377 private XmlElement CreateSinceNode (XmlDocument doc)
1379 XmlElement s = doc.CreateElement ("since");
1380 s.SetAttribute ("version", since);
1384 // STUBBING/UPDATING FUNCTIONS
1386 public void UpdateType (XmlElement root, TypeDefinition type)
1388 root.SetAttribute("Name", GetDocTypeName (type));
1389 root.SetAttribute("FullName", GetDocTypeFullName (type));
1391 foreach (MemberFormatter f in typeFormatters) {
1392 string element = "TypeSignature[@Language='" + f.Language + "']";
1393 string valueToUse = f.GetDeclaration (type);
1396 root.SelectNodes (element).Cast<XmlElement> ().ToArray (),
1397 x => x.GetAttribute ("Value") == valueToUse,
1398 x => x.SetAttribute ("Value", valueToUse),
1400 var node = WriteElementAttribute (root, element, "Language", f.Language, forceNewElement: true);
1401 var newnode = WriteElementAttribute (root, node, "Value", valueToUse);
1406 string assemblyInfoNodeFilter = MDocUpdater.HasDroppedNamespace () ? "[@apistyle='unified']" : "[not(@apistyle) or @apistyle='classic']";
1409 root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ().ToArray (),
1410 x => x.SelectSingleNode("AssemblyName").InnerText == type.Module.Assembly.Name.Name,
1411 x => WriteElementText(x, "AssemblyName", type.Module.Assembly.Name.Name),
1413 XmlElement ass = WriteElement(root, "AssemblyInfo", forceNewElement:true);
1415 if (MDocUpdater.HasDroppedNamespace ()) ass.SetAttribute ("apistyle", "unified");
1422 foreach(var ass in root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ())
1424 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1425 if (!no_assembly_versions) {
1426 UpdateAssemblyVersions (root, type, true);
1429 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1430 foreach (var version in versions)
1431 ass.RemoveChild (version);
1433 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1434 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1436 ClearElement(ass, "AssemblyCulture");
1439 // Why-oh-why do we put assembly attributes in each type file?
1440 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1441 // since they're outdated in current docs, and a waste of space.
1442 //MakeAttributes(ass, type.Assembly, true);
1443 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1444 if (assattrs != null)
1445 ass.RemoveChild(assattrs);
1447 NormalizeWhitespace(ass);
1450 if (type.IsGenericType ()) {
1451 MakeTypeParameters (root, type.GenericParameters, MDocUpdater.HasDroppedNamespace());
1453 ClearElement(root, "TypeParameters");
1456 if (type.BaseType != null) {
1457 XmlElement basenode = WriteElement(root, "Base");
1459 string basetypename = GetDocTypeFullName (type.BaseType);
1460 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1461 WriteElementText(root, "Base/BaseTypeName", basetypename);
1463 // Document how this type instantiates the generic parameters of its base type
1464 TypeReference origBase = type.BaseType.GetElementType ();
1465 if (origBase.IsGenericType ()) {
1466 ClearElement(basenode, "BaseTypeArguments");
1467 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1468 IList<TypeReference> baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1469 IList<GenericParameter> baseGenParams = origBase.GenericParameters;
1470 if (baseGenArgs.Count != baseGenParams.Count)
1471 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1472 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1473 GenericParameter param = baseGenParams [i];
1474 TypeReference value = baseGenArgs [i];
1476 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1477 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1478 bta.AppendChild(arg);
1479 arg.SetAttribute ("TypeParamName", param.Name);
1480 arg.InnerText = GetDocTypeFullName (value);
1484 ClearElement(root, "Base");
1487 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1488 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1489 List<string> interface_names = userInterfaces
1490 .Select (iface => GetDocTypeFullName (iface))
1494 XmlElement interfaces = WriteElement(root, "Interfaces");
1495 interfaces.RemoveAll();
1496 foreach (string iname in interface_names) {
1497 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1498 interfaces.AppendChild(iface);
1499 WriteElementText(iface, "InterfaceName", iname);
1502 ClearElement(root, "Interfaces");
1505 MakeAttributes (root, GetCustomAttributes (type), type);
1507 if (DocUtils.IsDelegate (type)) {
1508 MakeTypeParameters (root, type.GenericParameters, MDocUpdater.HasDroppedNamespace());
1509 MakeParameters(root, type.GetMethod("Invoke").Parameters);
1510 MakeReturnValue(root, type.GetMethod("Invoke"));
1513 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1514 MakeDocNode (typeInfo);
1516 if (!DocUtils.IsDelegate (type))
1517 WriteElement (root, "Members");
1519 OrderTypeNodes (root, root.ChildNodes);
1520 NormalizeWhitespace(root);
1523 static readonly string[] TypeNodeOrder = {
1527 "ThreadingSafetyStatement",
1528 "ThreadSafetyStatement",
1540 static void OrderTypeNodes (XmlNode member, XmlNodeList children)
1542 ReorderNodes (member, children, TypeNodeOrder);
1545 internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1547 List<T> l = new List<T> (list);
1552 private void UpdateMember (DocsNodeInfo info)
1554 XmlElement me = (XmlElement) info.Node;
1555 MemberReference mi = info.Member;
1557 foreach (MemberFormatter f in memberFormatters) {
1558 string element = "MemberSignature[@Language='" + f.Language + "']";
1560 var valueToUse = f.GetDeclaration (mi);
1563 me.SelectNodes (element).Cast<XmlElement> ().ToArray(),
1564 x => x.GetAttribute("Value") == valueToUse,
1565 x => x.SetAttribute ("Value", valueToUse),
1567 var node = WriteElementAttribute (me, element, "Language", f.Language, forceNewElement:true);
1568 var newNode = WriteElementAttribute (me, node, "Value", valueToUse);
1574 WriteElementText(me, "MemberType", GetMemberType(mi));
1576 if (!no_assembly_versions) {
1577 UpdateAssemblyVersions (me, mi, true);
1580 ClearElement (me, "AssemblyInfo");
1583 MakeAttributes (me, GetCustomAttributes (mi), mi.DeclaringType);
1585 MakeReturnValue(me, mi, MDocUpdater.HasDroppedNamespace());
1586 if (mi is MethodReference) {
1587 MethodReference mb = (MethodReference) mi;
1588 if (mb.IsGenericMethod ())
1589 MakeTypeParameters (me, mb.GenericParameters, MDocUpdater.HasDroppedNamespace());
1591 MakeParameters(me, mi, MDocUpdater.HasDroppedNamespace());
1594 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1595 WriteElementText(me, "MemberValue", fieldValue);
1597 info.Node = WriteElement (me, "Docs");
1599 OrderMemberNodes (me, me.ChildNodes);
1600 UpdateExtensionMethods (me, info);
1603 /// <summary>Adds an xml node, reusing the node if it's available</summary>
1604 /// <param name="relevant">The existing set of nodes</param>
1605 /// <param name="valueMatches">Checks to see if the node's value matches what you're trying to write.</param>
1606 /// <param name="setValue">Sets the node's value</param>
1607 /// <param name="makeNewNode">Creates a new node, if valueMatches returns false.</param>
1608 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode)
1610 bool shouldDuplicate = MDocUpdater.HasDroppedNamespace ();
1611 var styleToUse = shouldDuplicate ? ApiStyle.Unified : ApiStyle.Classic;
1612 var existing = relevant;
1614 bool addedOldApiStyle = false;
1616 if (shouldDuplicate) {
1617 existing = existing.Where (n => n.HasApiStyle (styleToUse)).ToArray ();
1618 foreach (var n in relevant.Where (n => n.DoesNotHaveApiStyle (styleToUse))) {
1619 if (valueMatches (n)) {
1623 n.AddApiStyle (ApiStyle.Classic);
1624 addedOldApiStyle = true;
1629 if (!existing.Any ()) {
1630 var newNode = makeNewNode ();
1631 if (shouldDuplicate && addedOldApiStyle) {
1632 newNode.AddApiStyle (ApiStyle.Unified);
1636 var itemToReuse = existing.First ();
1637 setValue (itemToReuse);
1639 if (shouldDuplicate && addedOldApiStyle) {
1640 itemToReuse.AddApiStyle (styleToUse);
1646 static readonly string[] MemberNodeOrder = {
1661 static void OrderMemberNodes (XmlNode member, XmlNodeList children)
1663 ReorderNodes (member, children, MemberNodeOrder);
1666 static void ReorderNodes (XmlNode node, XmlNodeList children, string[] ordering)
1668 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1669 for (int i = 0; i < ordering.Length; ++i) {
1670 for (int j = 0; j < children.Count; ++j) {
1671 XmlNode c = children [j];
1672 if (c.Name == ordering [i]) {
1673 newChildren.Add (c);
1677 if (newChildren.Count >= 0)
1678 node.PrependChild ((XmlNode) newChildren [0]);
1679 for (int i = 1; i < newChildren.Count; ++i) {
1680 XmlNode prev = (XmlNode) newChildren [i-1];
1681 XmlNode cur = (XmlNode) newChildren [i];
1682 node.RemoveChild (cur);
1683 node.InsertAfter (cur, prev);
1687 IEnumerable<string> GetCustomAttributes (MemberReference mi)
1689 IEnumerable<string> attrs = Enumerable.Empty<string>();
1691 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1693 attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1695 PropertyDefinition pd = mi as PropertyDefinition;
1697 if (pd.GetMethod != null)
1698 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1699 if (pd.SetMethod != null)
1700 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1703 EventDefinition ed = mi as EventDefinition;
1705 if (ed.AddMethod != null)
1706 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1707 if (ed.RemoveMethod != null)
1708 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1714 IEnumerable<string> GetCustomAttributes (IList<CustomAttribute> attributes, string prefix)
1716 foreach (CustomAttribute attribute in attributes.OrderBy (ca => ca.AttributeType.FullName)) {
1718 TypeDefinition attrType = attribute.AttributeType as TypeDefinition;
1719 if (attrType != null && !IsPublic (attrType))
1721 if (slashdocFormatter.GetName (attribute.AttributeType) == null)
1724 if (Array.IndexOf (IgnorableAttributes, attribute.AttributeType.FullName) >= 0)
1727 StringList fields = new StringList ();
1729 for (int i = 0; i < attribute.ConstructorArguments.Count; ++i) {
1730 CustomAttributeArgument argument = attribute.ConstructorArguments [i];
1731 fields.Add (MakeAttributesValueString (
1736 (from namedArg in attribute.Fields
1737 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value })
1739 (from namedArg in attribute.Properties
1740 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value }))
1741 .OrderBy (v => v.Name);
1742 foreach (var d in namedArgs)
1743 fields.Add (string.Format ("{0}={1}", d.Name,
1744 MakeAttributesValueString (d.Value, d.Type)));
1746 string a2 = String.Join(", ", fields.ToArray ());
1747 if (a2 != "") a2 = "(" + a2 + ")";
1749 string name = attribute.GetDeclaringType();
1750 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1751 yield return prefix + name + a2;
1755 static readonly string[] ValidExtensionMembers = {
1764 static readonly string[] ValidExtensionDocMembers = {
1770 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1772 MethodDefinition me = info.Member as MethodDefinition;
1775 if (info.Parameters.Count < 1)
1777 if (!DocUtils.IsExtensionMethod (me))
1780 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1781 XmlNode member = e.CloneNode (true);
1782 em.AppendChild (member);
1783 RemoveExcept (member, ValidExtensionMembers);
1784 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1785 WriteElementText (member, "MemberType", "ExtensionMethod");
1786 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1787 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1788 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1789 member.AppendChild (link);
1790 AddTargets (em, info);
1792 extensionMethods.Add (em);
1795 private static void RemoveExcept (XmlNode node, string[] except)
1799 MyXmlNodeList remove = null;
1800 foreach (XmlNode n in node.ChildNodes) {
1801 if (Array.BinarySearch (except, n.Name) < 0) {
1803 remove = new MyXmlNodeList ();
1808 foreach (XmlNode n in remove)
1809 node.RemoveChild (n);
1812 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1814 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1815 member.PrependChild (targets);
1816 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1817 AppendElementAttributeText (targets, "Target", "Type",
1818 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1821 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1822 IList<TypeReference> constraints = gp.Constraints;
1823 if (constraints.Count == 0)
1824 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1826 foreach (TypeReference c in constraints)
1827 AppendElementAttributeText(targets, "Target", "Type",
1828 slashdocFormatter.GetDeclaration (c));
1832 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1835 TypeDefinition type = field.DeclaringType.Resolve ();
1836 if (type != null && type.IsEnum) return false;
1838 if (type != null && type.IsGenericType ()) return false;
1839 if (!field.HasConstant)
1841 if (field.IsLiteral) {
1842 object val = field.Constant;
1843 if (val == null) value = "null";
1844 else if (val is Enum) value = val.ToString();
1845 else if (val is IFormattable) {
1846 value = ((IFormattable)val).ToString();
1848 value = "\"" + value + "\"";
1850 if (value != null && value != "")
1856 // XML HELPER FUNCTIONS
1858 internal static XmlElement WriteElement(XmlNode parent, string element, bool forceNewElement = false) {
1859 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1860 if (ret == null || forceNewElement) {
1861 string[] path = element.Split('/');
1862 foreach (string p in path) {
1863 ret = (XmlElement)parent.SelectSingleNode(p);
1864 if (ret == null || forceNewElement) {
1866 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1867 ename = ename.Substring(0, ename.IndexOf('['));
1868 ret = parent.OwnerDocument.CreateElement(ename);
1869 parent.AppendChild(ret);
1878 private static XmlElement WriteElementText(XmlNode parent, string element, string value, bool forceNewElement = false) {
1879 XmlElement node = WriteElement(parent, element, forceNewElement: forceNewElement);
1880 node.InnerText = value;
1884 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1886 XmlElement n = parent.OwnerDocument.CreateElement (element);
1887 parent.AppendChild (n);
1888 n.InnerText = value;
1892 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1894 XmlElement n = parent.OwnerDocument.CreateElement (element);
1895 parent.AppendChild (n);
1896 n.SetAttribute (attribute, value);
1900 internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
1902 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1903 dest.AppendChild (copy);
1907 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1908 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1911 node = WriteElement(parent, element);
1912 node.InnerText = value;
1914 private static XmlElement WriteElementAttribute(XmlElement parent, string element, string attribute, string value, bool forceNewElement = false) {
1915 XmlElement node = WriteElement(parent, element, forceNewElement:forceNewElement);
1916 return WriteElementAttribute (parent, node, attribute, value);
1918 private static XmlElement WriteElementAttribute(XmlElement parent, XmlElement node, string attribute, string value) {
1919 if (node.GetAttribute (attribute) != value) {
1920 node.SetAttribute (attribute, value);
1924 internal static void ClearElement(XmlElement parent, string name) {
1925 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1927 parent.RemoveChild(node);
1930 // DOCUMENTATION HELPER FUNCTIONS
1932 private void MakeDocNode (DocsNodeInfo info)
1934 List<GenericParameter> genericParams = info.GenericParameters;
1935 IList<ParameterDefinition> parameters = info.Parameters;
1936 TypeReference returntype = info.ReturnType;
1937 bool returnisreturn = info.ReturnIsReturn;
1938 XmlElement e = info.Node;
1939 bool addremarks = info.AddRemarks;
1941 WriteElementInitialText(e, "summary", "To be added.");
1943 if (parameters != null) {
1944 string[] values = new string [parameters.Count];
1945 for (int i = 0; i < values.Length; ++i)
1946 values [i] = parameters [i].Name;
1947 UpdateParameters (e, "param", values);
1950 if (genericParams != null) {
1951 string[] values = new string [genericParams.Count];
1952 for (int i = 0; i < values.Length; ++i)
1953 values [i] = genericParams [i].Name;
1954 UpdateParameters (e, "typeparam", values);
1957 string retnodename = null;
1958 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
1959 retnodename = returnisreturn ? "returns" : "value";
1960 string retnodename_other = !returnisreturn ? "returns" : "value";
1962 // If it has a returns node instead of a value node, change its name.
1963 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1964 if (retother != null) {
1965 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1966 foreach (XmlNode node in retother)
1967 retnode.AppendChild(node.CloneNode(true));
1968 e.ReplaceChild(retnode, retother);
1970 WriteElementInitialText(e, retnodename, "To be added.");
1973 ClearElement(e, "returns");
1974 ClearElement(e, "value");
1978 WriteElementInitialText(e, "remarks", "To be added.");
1980 if (exceptions.HasValue && info.Member != null &&
1981 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
1982 UpdateExceptions (e, info.Member);
1985 foreach (DocumentationImporter importer in importers)
1986 importer.ImportDocumentation (info);
1988 OrderDocsNodes (e, e.ChildNodes);
1989 NormalizeWhitespace(e);
1992 static readonly string[] DocsNodeOrder = {
1993 "typeparam", "param", "summary", "returns", "value", "remarks",
1996 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
1998 ReorderNodes (docs, children, DocsNodeOrder);
2002 private void UpdateParameters (XmlElement e, string element, string[] values)
2004 if (values != null) {
2005 XmlNode[] paramnodes = new XmlNode[values.Length];
2007 // Some documentation had param nodes with leading spaces.
2008 foreach (XmlElement paramnode in e.SelectNodes(element)){
2009 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
2012 // If a member has only one parameter, we can track changes to
2013 // the name of the parameter easily.
2014 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
2015 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
2018 bool reinsert = false;
2020 // Pick out existing and still-valid param nodes, and
2021 // create nodes for parameters not in the file.
2022 Hashtable seenParams = new Hashtable();
2023 for (int pi = 0; pi < values.Length; pi++) {
2024 string p = values [pi];
2027 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
2028 if (paramnodes[pi] != null) continue;
2030 XmlElement pe = e.OwnerDocument.CreateElement(element);
2031 pe.SetAttribute("name", p);
2032 pe.InnerText = "To be added.";
2033 paramnodes[pi] = pe;
2037 // Remove parameters that no longer exist and check all params are in the right order.
2039 MyXmlNodeList todelete = new MyXmlNodeList ();
2040 foreach (XmlElement paramnode in e.SelectNodes(element)) {
2041 string name = paramnode.GetAttribute("name");
2042 if (!seenParams.ContainsKey(name)) {
2043 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2044 Warning ("The following param node can only be deleted if the --delete option is given: ");
2045 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
2047 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
2048 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2052 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
2053 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2054 e.ParentNode.Attributes ["MemberName"].Value,
2057 Warning ("\tValue={0}", paramnode.OuterXml);
2059 todelete.Add (paramnode);
2064 if ((int)seenParams[name] != idx)
2070 foreach (XmlNode n in todelete) {
2071 n.ParentNode.RemoveChild (n);
2074 // Re-insert the parameter nodes at the top of the doc section.
2076 for (int pi = values.Length-1; pi >= 0; pi--)
2077 e.PrependChild(paramnodes[pi]);
2079 // Clear all existing param nodes
2080 foreach (XmlNode paramnode in e.SelectNodes(element)) {
2081 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2082 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
2083 Console.WriteLine(paramnode.OuterXml);
2085 paramnode.ParentNode.RemoveChild(paramnode);
2091 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
2093 string existingName = pe.GetAttribute ("name");
2094 pe.SetAttribute ("name", newName);
2095 if (existingName == newName)
2097 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
2098 if (paramref.GetAttribute ("name").Trim () == existingName)
2099 paramref.SetAttribute ("name", newName);
2102 class CrefComparer : XmlNodeComparer {
2104 public CrefComparer ()
2108 public override int Compare (XmlNode x, XmlNode y)
2110 string xType = x.Attributes ["cref"].Value;
2111 string yType = y.Attributes ["cref"].Value;
2112 string xNamespace = GetNamespace (xType);
2113 string yNamespace = GetNamespace (yType);
2115 int c = xNamespace.CompareTo (yNamespace);
2118 return xType.CompareTo (yType);
2121 static string GetNamespace (string type)
2123 int n = type.LastIndexOf ('.');
2125 return type.Substring (0, n);
2126 return string.Empty;
2130 private void UpdateExceptions (XmlNode docs, MemberReference member)
2132 string indent = new string (' ', 10);
2133 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
2134 string cref = slashdocFormatter.GetDeclaration (source.Exception);
2135 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
2138 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
2139 e.SetAttribute ("cref", cref);
2140 e.InnerXml = "To be added; from:\n" + indent + "<see cref=\"" +
2141 string.Join ("\" />,\n" + indent + "<see cref=\"",
2142 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
2143 .OrderBy (s => s)) +
2145 docs.AppendChild (e);
2147 SortXmlNodes (docs, docs.SelectNodes ("exception"),
2148 new CrefComparer ());
2151 private static void NormalizeWhitespace(XmlElement e) {
2152 // Remove all text and whitespace nodes from the element so it
2153 // is outputted with nice indentation and no blank lines.
2154 ArrayList deleteNodes = new ArrayList();
2155 foreach (XmlNode n in e)
2156 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2158 foreach (XmlNode n in deleteNodes)
2159 n.ParentNode.RemoveChild(n);
2162 private static bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
2164 TypeDefinition type = member as TypeDefinition;
2166 type = member.DeclaringType as TypeDefinition;
2167 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
2170 private static string GetAssemblyVersion (AssemblyDefinition assembly)
2172 return assembly.Name.Version.ToString();
2175 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
2177 XmlElement av = (XmlElement) root.SelectSingleNode ("AssemblyVersions");
2179 // AssemblyVersions is not part of the spec
2180 root.RemoveChild (av);
2183 string oldNodeFilter = "AssemblyInfo[not(@apistyle) or @apistyle='classic']";
2184 string newNodeFilter = "AssemblyInfo[@apistyle='unified']";
2185 string thisNodeFilter = MDocUpdater.HasDroppedNamespace () ? newNodeFilter : oldNodeFilter;
2186 string thatNodeFilter = MDocUpdater.HasDroppedNamespace () ? oldNodeFilter : newNodeFilter;
2188 XmlElement e = (XmlElement) root.SelectSingleNode (thisNodeFilter);
2190 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2192 if (MDocUpdater.HasDroppedNamespace ()) {
2193 e.SetAttribute ("apistyle", "unified");
2196 root.AppendChild(e);
2199 var thatNode = (XmlElement) root.SelectSingleNode (thatNodeFilter);
2200 if (MDocUpdater.HasDroppedNamespace () && thatNode != null) {
2201 // there's a classic node, we should add apistyles
2202 e.SetAttribute ("apistyle", "unified");
2203 thatNode.SetAttribute ("apistyle", "classic");
2206 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
2207 .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
2209 // matches.Count > 0 && add: ignore -- already present
2210 if (matches.Count > 0 && !add) {
2211 foreach (XmlNode c in matches)
2214 else if (matches.Count == 0 && add) {
2215 foreach (string sv in assemblyVersions) {
2216 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2222 // matches.Count == 0 && !add: ignore -- already not present
2223 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2224 SortXmlNodes (e, avs, new VersionComparer ());
2226 bool anyNodesLeft = avs.Count != 0;
2227 if (!anyNodesLeft) {
2228 e.ParentNode.RemoveChild (e);
2230 return anyNodesLeft;
2233 // FIXME: get TypeReferences instead of string comparison?
2234 private static string[] IgnorableAttributes = {
2235 // Security related attributes
2236 "System.Reflection.AssemblyKeyFileAttribute",
2237 "System.Reflection.AssemblyDelaySignAttribute",
2238 // Present in @RefType
2239 "System.Runtime.InteropServices.OutAttribute",
2240 // For naming the indexer to use when not using indexers
2241 "System.Reflection.DefaultMemberAttribute",
2242 // for decimal constants
2243 "System.Runtime.CompilerServices.DecimalConstantAttribute",
2244 // compiler generated code
2245 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
2246 // more compiler generated code, e.g. iterator methods
2247 "System.Diagnostics.DebuggerHiddenAttribute",
2248 "System.Runtime.CompilerServices.FixedBufferAttribute",
2249 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
2250 // extension methods
2251 "System.Runtime.CompilerServices.ExtensionAttribute",
2252 // Used to differentiate 'object' from C#4 'dynamic'
2253 "System.Runtime.CompilerServices.DynamicAttribute",
2256 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes, TypeReference t=null)
2258 if (!attributes.Any ()) {
2259 ClearElement (root, "Attributes");
2263 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2267 e = root.OwnerDocument.CreateElement("Attributes");
2269 foreach (string attribute in attributes) {
2270 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2273 WriteElementText(ae, "AttributeName", attribute);
2276 if (e.ParentNode == null)
2277 root.AppendChild(e);
2279 NormalizeWhitespace(e);
2282 public static string MakeAttributesValueString (object v, TypeReference valueType)
2286 if (valueType.FullName == "System.Type") {
2287 var vTypeRef = v as TypeReference;
2288 if (vTypeRef != null)
2289 return "typeof(" + NativeTypeManager.GetTranslatedName (vTypeRef) + ")"; // TODO: drop NS handling
2291 return "typeof(" + v.ToString () + ")";
2293 if (valueType.FullName == "System.String")
2294 return "\"" + v.ToString () + "\"";
2295 if (valueType.FullName == "System.Char")
2296 return "'" + v.ToString () + "'";
2298 return (bool)v ? "true" : "false";
2299 TypeDefinition valueDef = valueType.Resolve ();
2300 if (valueDef == null || !valueDef.IsEnum)
2301 return v.ToString ();
2302 string typename = GetDocTypeFullName (valueType);
2303 var values = GetEnumerationValues (valueDef);
2304 long c = ToInt64 (v);
2305 if (values.ContainsKey (c))
2306 return typename + "." + values [c];
2307 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
2308 return string.Join (" | ",
2309 (from i in values.Keys
2311 select typename + "." + values [i])
2312 .DefaultIfEmpty (v.ToString ()).ToArray ());
2314 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
2317 private static Dictionary<long, string> GetEnumerationValues (TypeDefinition type)
2319 var values = new Dictionary<long, string> ();
2321 (from f in type.Fields
2322 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
2324 values [ToInt64 (f.Constant)] = f.Name;
2329 static long ToInt64 (object value)
2332 return (long) (ulong) value;
2333 return Convert.ToInt64 (value);
2336 private void MakeParameters (XmlElement root, IList<ParameterDefinition> parameters, bool shouldDuplicateWithNew=false)
2338 XmlElement e = WriteElement(root, "Parameters");
2341 foreach (ParameterDefinition p in parameters) {
2345 var ptype = GetDocParameterType (p.ParameterType);
2346 var newPType = ptype;
2348 if (MDocUpdater.SwitchingToMagicTypes) {
2349 newPType = NativeTypeManager.ConvertFromNativeType (ptype);
2352 // now find the existing node, if it's there so we can reuse it.
2353 var nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2354 .Cast<XmlElement> ().Where (x => x.GetAttribute ("Name") == p.Name)
2357 if (nodes.Count () == 0) {
2358 // wasn't found, let's make sure it wasn't just cause the param name was changed
2359 nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2360 .Cast<XmlElement> ()
2361 .Skip (i) // this makes sure we don't inadvertently "reuse" nodes when adding new ones
2362 .Where (x => x.GetAttribute ("Name") != p.Name && (x.GetAttribute ("Type") == ptype || x.GetAttribute ("Type") == newPType))
2363 .Take(1) // there might be more than one that meets this parameter ... only take the first.
2368 x => x.GetAttribute ("Type") == ptype,
2369 x => x.SetAttribute ("Type", ptype),
2371 pe = root.OwnerDocument.CreateElement ("Parameter");
2374 pe.SetAttribute ("Name", p.Name);
2375 pe.SetAttribute ("Type", ptype);
2376 if (p.ParameterType is ByReferenceType) {
2378 pe.SetAttribute ("RefType", "out");
2380 pe.SetAttribute ("RefType", "ref");
2383 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
2391 private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams, bool shouldDuplicateWithNew)
2393 if (typeParams == null || typeParams.Count == 0) {
2394 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2396 root.RemoveChild (f);
2399 XmlElement e = WriteElement(root, "TypeParameters");
2401 var nodes = e.SelectNodes ("TypeParameter").Cast<XmlElement> ().ToArray ();
2403 foreach (GenericParameter t in typeParams) {
2405 IList<TypeReference> constraints = t.Constraints;
2406 GenericParameterAttributes attrs = t.Attributes;
2412 var baseType = e.SelectSingleNode("BaseTypeName");
2413 // TODO: should this comparison take into account BaseTypeName?
2414 return x.GetAttribute("Name") == t.Name;
2416 x => {}, // no additional action required
2419 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2421 pe.SetAttribute("Name", t.Name);
2422 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""), t.DeclaringType);
2423 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2424 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2432 ce = root.OwnerDocument.CreateElement ("Constraints");
2434 pe.AppendChild (ce);
2435 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2436 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2437 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2438 AppendElementText (ce, "ParameterAttribute", "Covariant");
2439 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2440 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2441 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2442 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2443 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2444 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2445 foreach (TypeReference c in constraints) {
2446 TypeDefinition cd = c.Resolve ();
2447 AppendElementText (ce,
2448 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2449 GetDocTypeFullName (c));
2457 private void MakeParameters (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew)
2459 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2460 MakeParameters (root, ((MethodDefinition)mi).Parameters, shouldDuplicateWithNew);
2461 else if (mi is MethodDefinition) {
2462 MethodDefinition mb = (MethodDefinition) mi;
2463 IList<ParameterDefinition> parameters = mb.Parameters;
2464 MakeParameters(root, parameters, shouldDuplicateWithNew);
2465 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2466 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2467 p.SetAttribute ("RefType", "this");
2470 else if (mi is PropertyDefinition) {
2471 IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
2472 if (parameters.Count > 0)
2473 MakeParameters(root, parameters, shouldDuplicateWithNew);
2477 else if (mi is FieldDefinition) return;
2478 else if (mi is EventDefinition) return;
2479 else throw new ArgumentException();
2482 internal static string GetDocParameterType (TypeReference type)
2484 return GetDocTypeFullName (type).Replace ("@", "&");
2487 private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes, bool shouldDuplicateWithNew=false)
2489 XmlElement e = WriteElement(root, "ReturnValue");
2490 var valueToUse = GetDocTypeFullName (type);
2492 AddXmlNode (e.SelectNodes("ReturnType").Cast<XmlElement> ().ToArray (),
2493 x => x.InnerText == valueToUse,
2494 x => x.InnerText = valueToUse,
2496 var newNode = WriteElementText(e, "ReturnType", valueToUse, forceNewElement: true);
2497 if (attributes != null)
2498 MakeAttributes(e, GetCustomAttributes (attributes, ""), type);
2504 private void MakeReturnValue (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew=false)
2506 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2508 else if (mi is MethodDefinition)
2509 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes, shouldDuplicateWithNew);
2510 else if (mi is PropertyDefinition)
2511 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null, shouldDuplicateWithNew);
2512 else if (mi is FieldDefinition)
2513 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null, shouldDuplicateWithNew);
2514 else if (mi is EventDefinition)
2515 MakeReturnValue (root, ((EventDefinition)mi).EventType, null, shouldDuplicateWithNew);
2517 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2520 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2522 MemberReference mi = info.Member;
2523 if (mi is TypeDefinition) return null;
2525 string sigs = memberFormatters [0].GetDeclaration (mi);
2526 if (sigs == null) return null; // not publicly visible
2528 // no documentation for property/event accessors. Is there a better way of doing this?
2529 if (mi.Name.StartsWith("get_")) return null;
2530 if (mi.Name.StartsWith("set_")) return null;
2531 if (mi.Name.StartsWith("add_")) return null;
2532 if (mi.Name.StartsWith("remove_")) return null;
2533 if (mi.Name.StartsWith("raise_")) return null;
2535 XmlElement me = doc.CreateElement("Member");
2536 me.SetAttribute("MemberName", GetMemberName (mi));
2540 if (exceptions.HasValue &&
2541 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2542 UpdateExceptions (info.Node, info.Member);
2544 if (since != null) {
2545 XmlNode docs = me.SelectSingleNode("Docs");
2546 docs.AppendChild (CreateSinceNode (doc));
2552 internal static string GetMemberName (MemberReference mi)
2554 MethodDefinition mb = mi as MethodDefinition;
2556 PropertyDefinition pi = mi as PropertyDefinition;
2559 return DocUtils.GetPropertyName (pi);
2561 StringBuilder sb = new StringBuilder (mi.Name.Length);
2562 if (!DocUtils.IsExplicitlyImplemented (mb))
2563 sb.Append (mi.Name);
2565 TypeReference iface;
2566 MethodReference ifaceMethod;
2567 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2568 sb.Append (GetDocTypeFullName (iface));
2570 sb.Append (ifaceMethod.Name);
2572 if (mb.IsGenericMethod ()) {
2573 IList<GenericParameter> typeParams = mb.GenericParameters;
2574 if (typeParams.Count > 0) {
2576 sb.Append (typeParams [0].Name);
2577 for (int i = 1; i < typeParams.Count; ++i)
2578 sb.Append (",").Append (typeParams [i].Name);
2582 return sb.ToString ();
2585 /// SIGNATURE GENERATION FUNCTIONS
2586 internal static bool IsPrivate (MemberReference mi)
2588 return memberFormatters [0].GetDeclaration (mi) == null;
2591 internal static string GetMemberType (MemberReference mi)
2593 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2594 return "Constructor";
2595 if (mi is MethodDefinition)
2597 if (mi is PropertyDefinition)
2599 if (mi is FieldDefinition)
2601 if (mi is EventDefinition)
2603 throw new ArgumentException();
2606 private static string GetDocTypeName (TypeReference type)
2608 return docTypeFormatter.GetName (type);
2611 internal static string GetDocTypeFullName (TypeReference type)
2613 return DocTypeFullMemberFormatter.Default.GetName (type);
2616 internal static string GetXPathForMember (DocumentationMember member)
2618 StringBuilder xpath = new StringBuilder ();
2619 xpath.Append ("//Members/Member[@MemberName=\"")
2620 .Append (member.MemberName)
2622 if (member.Parameters != null && member.Parameters.Count > 0) {
2623 xpath.Append ("/Parameters[count(Parameter) = ")
2624 .Append (member.Parameters.Count);
2625 for (int i = 0; i < member.Parameters.Count; ++i) {
2626 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2627 xpath.Append (member.Parameters [i]);
2628 xpath.Append ("\"");
2630 xpath.Append ("]/..");
2632 return xpath.ToString ();
2635 public static string GetXPathForMember (XPathNavigator member)
2637 StringBuilder xpath = new StringBuilder ();
2638 xpath.Append ("//Type[@FullName=\"")
2639 .Append (member.SelectSingleNode ("../../@FullName").Value)
2641 xpath.Append ("Members/Member[@MemberName=\"")
2642 .Append (member.SelectSingleNode ("@MemberName").Value)
2644 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2645 if (parameters.Count > 0) {
2646 xpath.Append ("/Parameters[count(Parameter) = ")
2647 .Append (parameters.Count);
2649 while (parameters.MoveNext ()) {
2651 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2652 xpath.Append (parameters.Current.Value);
2653 xpath.Append ("\"");
2655 xpath.Append ("]/..");
2657 return xpath.ToString ();
2660 public static string GetXPathForMember (MemberReference member)
2662 StringBuilder xpath = new StringBuilder ();
2663 xpath.Append ("//Type[@FullName=\"")
2664 .Append (member.DeclaringType.FullName)
2666 xpath.Append ("Members/Member[@MemberName=\"")
2667 .Append (GetMemberName (member))
2670 IList<ParameterDefinition> parameters = null;
2671 if (member is MethodDefinition)
2672 parameters = ((MethodDefinition) member).Parameters;
2673 else if (member is PropertyDefinition) {
2674 parameters = ((PropertyDefinition) member).Parameters;
2676 if (parameters != null && parameters.Count > 0) {
2677 xpath.Append ("/Parameters[count(Parameter) = ")
2678 .Append (parameters.Count);
2679 for (int i = 0; i < parameters.Count; ++i) {
2680 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2681 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2682 xpath.Append ("\"");
2684 xpath.Append ("]/..");
2686 return xpath.ToString ();
2690 static class CecilExtensions {
2691 public static string GetDeclaringType(this CustomAttribute attribute)
2693 var type = attribute.Constructor.DeclaringType;
2694 var typeName = type.FullName;
2696 string translatedType = NativeTypeManager.GetTranslatedName (type);
2697 return translatedType;
2700 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
2702 foreach (var c in type.Methods.Where (m => m.IsConstructor))
2703 yield return (MemberReference) c;
2704 foreach (var e in type.Events)
2705 yield return (MemberReference) e;
2706 foreach (var f in type.Fields)
2707 yield return (MemberReference) f;
2708 foreach (var m in type.Methods.Where (m => !m.IsConstructor))
2709 yield return (MemberReference) m;
2710 foreach (var t in type.NestedTypes)
2711 yield return (MemberReference) t;
2712 foreach (var p in type.Properties)
2713 yield return (MemberReference) p;
2716 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
2718 return GetMembers (type).Where (m => m.Name == member);
2721 public static MemberReference GetMember (this TypeDefinition type, string member)
2723 return GetMembers (type, member).EnsureZeroOrOne ();
2726 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2728 if (source.Count () > 1)
2729 throw new InvalidOperationException ("too many matches");
2730 return source.FirstOrDefault ();
2733 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2736 .Where (m => m.Name == method)
2737 .EnsureZeroOrOne ();
2740 public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
2742 TypeDefinition def = type as TypeDefinition;
2744 return new MemberReference [0];
2745 CustomAttribute defMemberAttr = def.CustomAttributes
2746 .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
2747 if (defMemberAttr == null)
2748 return new MemberReference [0];
2749 string name = (string) defMemberAttr.ConstructorArguments [0].Value;
2750 return def.Properties
2751 .Where (p => p.Name == name)
2752 .Select (p => (MemberReference) p);
2755 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2757 return assembly.Modules.SelectMany (md => md.GetAllTypes ());
2760 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2762 return GetTypes (assembly)
2763 .Where (td => td.FullName == type)
2764 .EnsureZeroOrOne ();
2767 public static bool IsGenericType (this TypeReference type)
2769 return type.GenericParameters.Count > 0;
2772 public static bool IsGenericMethod (this MethodReference method)
2774 return method.GenericParameters.Count > 0;
2777 public static MemberReference Resolve (this MemberReference member)
2779 FieldReference fr = member as FieldReference;
2781 return fr.Resolve ();
2782 MethodReference mr = member as MethodReference;
2784 return mr.Resolve ();
2785 TypeReference tr = member as TypeReference;
2787 return tr.Resolve ();
2788 PropertyReference pr = member as PropertyReference;
2791 EventReference er = member as EventReference;
2794 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2797 public static TypeReference GetUnderlyingType (this TypeDefinition type)
2801 return type.Fields.First (f => f.Name == "value__").FieldType;
2804 public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
2806 return self.Types.SelectMany (t => t.GetAllTypes ());
2809 static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
2813 if (!self.HasNestedTypes)
2816 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
2826 static class DocUtils {
2828 public static bool DoesNotHaveApiStyle(this XmlElement element, ApiStyle style) {
2829 string styleString = style.ToString ().ToLower ();
2830 string apistylevalue = element.GetAttribute ("apistyle");
2831 return apistylevalue != styleString || string.IsNullOrWhiteSpace(apistylevalue);
2833 public static bool HasApiStyle(this XmlElement element, ApiStyle style) {
2834 string styleString = style.ToString ().ToLower ();
2835 return element.GetAttribute ("apistyle") == styleString;
2837 public static void AddApiStyle(this XmlElement element, ApiStyle style) {
2838 string styleString = style.ToString ().ToLower ();
2839 var existingValue = element.GetAttribute ("apistyle");
2840 if (string.IsNullOrWhiteSpace (existingValue) || existingValue != styleString) {
2841 element.SetAttribute ("apistyle", styleString);
2845 public static bool IsExplicitlyImplemented (MethodDefinition method)
2847 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2850 public static string GetTypeDotMember (string name)
2852 int startType, startMethod;
2853 startType = startMethod = -1;
2854 for (int i = 0; i < name.Length; ++i) {
2855 if (name [i] == '.') {
2856 startType = startMethod;
2860 return name.Substring (startType+1);
2863 public static string GetMember (string name)
2865 int i = name.LastIndexOf ('.');
2868 return name.Substring (i+1);
2871 public static void GetInfoForExplicitlyImplementedMethod (
2872 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2876 if (method.Overrides.Count != 1)
2877 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2878 iface = method.Overrides [0].DeclaringType;
2879 ifaceMethod = method.Overrides [0];
2882 public static string GetPropertyName (PropertyDefinition pi)
2884 // Issue: (g)mcs-generated assemblies that explicitly implement
2885 // properties don't specify the full namespace, just the
2886 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2887 MethodDefinition method = pi.GetMethod;
2889 method = pi.SetMethod;
2890 if (!IsExplicitlyImplemented (method))
2893 // Need to determine appropriate namespace for this member.
2894 TypeReference iface;
2895 MethodReference ifaceMethod;
2896 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2897 return string.Join (".", new string[]{
2898 DocTypeFullMemberFormatter.Default.GetName (iface),
2899 GetMember (pi.Name)});
2902 public static string GetNamespace (TypeReference type)
2904 if (type.GetElementType ().IsNested)
2905 type = type.GetElementType ();
2906 while (type != null && type.IsNested)
2907 type = type.DeclaringType;
2909 return string.Empty;
2911 string typeNS = type.Namespace;
2913 // first, make sure this isn't a type reference to another assembly/module
2915 bool isInAssembly = MDocUpdater.IsInAssemblies(type.Module.Name);
2916 if (isInAssembly && MDocUpdater.HasDroppedNamespace () && !typeNS.StartsWith ("System")) {
2917 typeNS = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typeNS);
2922 public static string PathCombine (string dir, string path)
2928 return Path.Combine (dir, path);
2931 public static bool IsExtensionMethod (MethodDefinition method)
2934 method.CustomAttributes
2935 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2936 && method.DeclaringType.CustomAttributes
2937 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
2940 public static bool IsDelegate (TypeDefinition type)
2942 TypeReference baseRef = type.BaseType;
2943 if (baseRef == null)
2945 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2946 baseRef.FullName == "System.MulticastDelegate";
2949 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2951 List<TypeReference> decls = new List<TypeReference> ();
2953 while (type.DeclaringType != null) {
2954 decls.Add (type.DeclaringType);
2955 type = type.DeclaringType;
2961 public static int GetGenericArgumentCount (TypeReference type)
2963 GenericInstanceType inst = type as GenericInstanceType;
2965 ? inst.GenericArguments.Count
2966 : type.GenericParameters.Count;
2969 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
2971 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
2972 List<TypeReference> userInterfaces = new List<TypeReference> ();
2973 foreach (TypeReference iface in type.Interfaces) {
2974 TypeReference lookup = iface.Resolve () ?? iface;
2975 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
2976 userInterfaces.Add (iface);
2978 return userInterfaces;
2981 private static string GetQualifiedTypeName (TypeReference type)
2983 return "[" + type.Scope.Name + "]" + type.FullName;
2986 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
2988 HashSet<string> inheritedInterfaces = new HashSet<string> ();
2989 Action<TypeDefinition> a = null;
2991 if (t == null) return;
2992 foreach (TypeReference r in t.Interfaces) {
2993 inheritedInterfaces.Add (GetQualifiedTypeName (r));
2997 TypeReference baseRef = type.BaseType;
2998 while (baseRef != null) {
2999 TypeDefinition baseDef = baseRef.Resolve ();
3000 if (baseDef != null) {
3002 baseRef = baseDef.BaseType;
3007 foreach (TypeReference r in type.Interfaces)
3009 return inheritedInterfaces;
3013 class DocsNodeInfo {
3014 public DocsNodeInfo (XmlElement node)
3019 public DocsNodeInfo (XmlElement node, TypeDefinition type)
3025 public DocsNodeInfo (XmlElement node, MemberReference member)
3028 SetMemberInfo (member);
3031 void SetType (TypeDefinition type)
3034 throw new ArgumentNullException ("type");
3036 GenericParameters = new List<GenericParameter> (type.GenericParameters);
3037 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
3038 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
3039 for (int i = 0; i < declTypes.Count - 1; ++i) {
3040 int remove = System.Math.Min (maxGenArgs,
3041 DocUtils.GetGenericArgumentCount (declTypes [i]));
3042 maxGenArgs -= remove;
3043 while (remove-- > 0)
3044 GenericParameters.RemoveAt (0);
3046 if (DocUtils.IsDelegate (type)) {
3047 Parameters = type.GetMethod("Invoke").Parameters;
3048 ReturnType = type.GetMethod("Invoke").ReturnType;
3049 ReturnIsReturn = true;
3053 void SetMemberInfo (MemberReference member)
3056 throw new ArgumentNullException ("member");
3057 ReturnIsReturn = true;
3061 if (member is MethodReference ) {
3062 MethodReference mr = (MethodReference) member;
3063 Parameters = mr.Parameters;
3064 if (mr.IsGenericMethod ()) {
3065 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
3068 else if (member is PropertyDefinition) {
3069 Parameters = ((PropertyDefinition) member).Parameters;
3072 if (member is MethodDefinition) {
3073 ReturnType = ((MethodDefinition) member).ReturnType;
3074 } else if (member is PropertyDefinition) {
3075 ReturnType = ((PropertyDefinition) member).PropertyType;
3076 ReturnIsReturn = false;
3079 // no remarks section for enum members
3080 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
3084 public TypeReference ReturnType;
3085 public List<GenericParameter> GenericParameters;
3086 public IList<ParameterDefinition> Parameters;
3087 public bool ReturnIsReturn;
3088 public XmlElement Node;
3089 public bool AddRemarks = true;
3090 public MemberReference Member;
3091 public TypeDefinition Type;
3093 public override string ToString ()
3095 return string.Format ("{0} - {1} - {2}", Type, Member, Node == null ? "no xml" : "with xml");
3099 class DocumentationEnumerator {
3101 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3103 return GetDocumentationTypes (assembly, forTypes, null);
3106 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3108 foreach (TypeDefinition type in assembly.GetTypes()) {
3109 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
3111 if (seen != null && seen.Contains (type.FullName))
3114 foreach (TypeDefinition nested in type.NestedTypes)
3115 yield return nested;
3119 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3121 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
3122 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
3123 oldmember.RemoveAttribute ("__monodocer-seen__");
3126 MemberReference m = GetMember (type, new DocumentationMember (oldmember));
3128 yield return new DocsNodeInfo (oldmember);
3131 yield return new DocsNodeInfo (oldmember, m);
3136 protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
3138 string membertype = member.MemberType;
3140 string returntype = member.ReturnType;
3142 string docName = member.MemberName;
3144 string[] docTypeParams = GetTypeParameters (docName);
3146 // If we're using 'magic types', then we might get false positives ... in those cases, we keep searching
3147 MemberReference likelyCandidate = null;
3149 // Loop through all members in this type with the same name
3150 var reflectedMembers = GetReflectionMembers (type, docName).ToArray ();
3151 foreach (MemberReference mi in reflectedMembers) {
3152 bool matchedMagicType = false;
3153 if (mi is TypeDefinition) continue;
3154 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
3156 if (MDocUpdater.IsPrivate (mi))
3159 IList<ParameterDefinition> pis = null;
3160 string[] typeParams = null;
3161 if (mi is MethodDefinition) {
3162 MethodDefinition mb = (MethodDefinition) mi;
3163 pis = mb.Parameters;
3164 if (docTypeParams != null && mb.IsGenericMethod ()) {
3165 IList<GenericParameter> args = mb.GenericParameters;
3166 if (args.Count == docTypeParams.Length) {
3167 typeParams = args.Select (p => p.Name).ToArray ();
3171 else if (mi is PropertyDefinition)
3172 pis = ((PropertyDefinition)mi).Parameters;
3174 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
3175 int pcount = pis == null ? 0 : pis.Count;
3176 if (mcount != pcount)
3179 MethodDefinition mDef = mi as MethodDefinition;
3180 if (mDef != null && !mDef.IsConstructor) {
3181 // Casting operators can overload based on return type.
3182 string rtype = GetReplacedString (
3183 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
3184 typeParams, docTypeParams);
3185 string originalRType = rtype;
3186 if (MDocUpdater.SwitchingToMagicTypes) {
3187 rtype = NativeTypeManager.ConvertFromNativeType (rtype);
3190 if ((returntype != rtype && originalRType == rtype) ||
3191 (MDocUpdater.SwitchingToMagicTypes && returntype != originalRType && returntype != rtype && originalRType != rtype)) {
3195 if (originalRType != rtype)
3196 matchedMagicType = true;
3202 for (int i = 0; i < pis.Count; i++) {
3203 string paramType = GetReplacedString (
3204 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
3205 typeParams, docTypeParams);
3207 // if magictypes, replace paramType to "classic value" ... so the comparison works
3208 string originalParamType = paramType;
3209 if (MDocUpdater.SwitchingToMagicTypes) {
3210 paramType = NativeTypeManager.ConvertFromNativeType (paramType);
3213 string xmlMemberType = member.Parameters [i];
3214 if ((!paramType.Equals(xmlMemberType) && paramType.Equals(originalParamType)) ||
3215 (MDocUpdater.SwitchingToMagicTypes && !originalParamType.Equals(xmlMemberType) && !paramType.Equals(xmlMemberType) && !paramType.Equals(originalParamType))) {
3217 // did not match ... if we're dropping the namespace, and the paramType has the dropped
3218 // namespace, we should see if it matches when added
3219 bool stillDoesntMatch = true;
3220 if (MDocUpdater.HasDroppedNamespace() && paramType.StartsWith (MDocUpdater.droppedNamespace)) {
3221 string withDroppedNs = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, xmlMemberType);
3223 stillDoesntMatch = withDroppedNs != paramType;
3226 if (stillDoesntMatch) {
3232 if (originalParamType != paramType)
3233 matchedMagicType = true;
3235 if (!good) continue;
3237 if (MDocUpdater.SwitchingToMagicTypes && likelyCandidate == null && matchedMagicType) {
3238 // we matched this on a magic type conversion ... let's keep going to see if there's another one we should look at that matches more closely
3239 likelyCandidate = mi;
3246 return likelyCandidate;
3249 static string[] GetTypeParameters (string docName)
3251 if (docName [docName.Length-1] != '>')
3253 StringList types = new StringList ();
3254 int endToken = docName.Length-2;
3255 int i = docName.Length-2;
3257 if (docName [i] == ',' || docName [i] == '<') {
3258 types.Add (docName.Substring (i + 1, endToken - i));
3261 if (docName [i] == '<')
3266 return types.ToArray ();
3269 protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
3271 // In case of dropping the namespace, we have to remove the dropped NS
3272 // so that docName will match what's in the assembly/type
3273 if (MDocUpdater.HasDroppedNamespace () && docName.StartsWith(MDocUpdater.droppedNamespace + ".")) {
3274 int droppedNsLength = MDocUpdater.droppedNamespace.Length;
3275 docName = docName.Substring (droppedNsLength + 1, docName.Length - droppedNsLength - 1);
3278 // need to worry about 4 forms of //@MemberName values:
3279 // 1. "Normal" (non-generic) member names: GetEnumerator
3281 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
3282 // - try as-is, and try type.member (due to "kludge" for property
3284 // 3. "Normal" Generic member names: Sort<T> (CSC)
3285 // - need to remove generic parameters --> "Sort"
3286 // 4. Explicitly-implemented interface members for generic interfaces:
3287 // -- System.Collections.Generic.IEnumerable<T>.Current
3288 // - Try as-is, and try type.member, *keeping* the generic parameters.
3289 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
3290 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
3291 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
3292 // this as (1) or (2).
3293 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
3295 foreach (MemberReference mi in type.GetMembers (docName))
3297 if (CountChars (docName, '.') > 0)
3298 // might be a property; try only type.member instead of
3299 // namespace.type.member.
3300 foreach (MemberReference mi in
3301 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
3308 int startLt, startType, startMethod;
3309 startLt = startType = startMethod = -1;
3310 for (int i = 0; i < docName.Length; ++i) {
3311 switch (docName [i]) {
3320 if (numLt == 0 && (i + 1) < docName.Length)
3321 // there's another character in docName, so this <...> sequence is
3322 // probably part of a generic type -- case 4.
3326 startType = startMethod;
3332 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
3334 foreach (MemberReference mi in type.GetMembers (refName))
3338 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
3341 // If we _still_ haven't found it, we've hit another generic naming issue:
3342 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
3343 // explicitly-implemented METHOD names (not properties), e.g.
3344 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
3345 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
3346 // which the XML docs will contain.
3348 // Alas, we can't derive the Mono name from docName, so we need to iterate
3349 // over all member names, convert them into CSC format, and compare... :-(
3352 foreach (MemberReference mi in type.GetMembers ()) {
3353 if (MDocUpdater.GetMemberName (mi) == docName)
3358 static string GetReplacedString (string typeName, string[] from, string[] to)
3362 for (int i = 0; i < from.Length; ++i)
3363 typeName = typeName.Replace (from [i], to [i]);
3367 private static int CountChars (string s, char c)
3370 for (int i = 0; i < s.Length; ++i) {
3378 class EcmaDocumentationEnumerator : DocumentationEnumerator {
3383 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
3386 this.ecmadocs = ecmaDocs;
3389 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3391 HashSet<string> seen = new HashSet<string> ();
3392 return GetDocumentationTypes (assembly, forTypes, seen)
3393 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
3396 new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3399 while (ecmadocs.Read ()) {
3400 switch (ecmadocs.Name) {
3402 if (typeDepth == -1)
3403 typeDepth = ecmadocs.Depth;
3404 if (ecmadocs.NodeType != XmlNodeType.Element)
3406 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
3408 string typename = ecmadocs.GetAttribute ("FullName");
3409 string typename2 = MDocUpdater.GetTypeFileName (typename);
3410 if (forTypes != null &&
3411 forTypes.BinarySearch (typename) < 0 &&
3412 typename != typename2 &&
3413 forTypes.BinarySearch (typename2) < 0)
3416 if ((t = assembly.GetType (typename)) == null &&
3417 (t = assembly.GetType (typename2)) == null)
3419 seen.Add (typename);
3420 if (typename != typename2)
3421 seen.Add (typename2);
3422 Console.WriteLine (" Import: {0}", t.FullName);
3423 if (ecmadocs.Name != "Docs") {
3424 int depth = ecmadocs.Depth;
3425 while (ecmadocs.Read ()) {
3426 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
3430 if (!ecmadocs.IsStartElement ("Docs"))
3431 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
3441 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3443 return GetMembers (basefile, type)
3444 .Concat (base.GetDocumentationMembers (basefile, type));
3447 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
3449 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
3452 if (ecmadocs.IsEmptyElement)
3455 int membersDepth = ecmadocs.Depth;
3457 while (go && ecmadocs.Read ()) {
3458 switch (ecmadocs.Name) {
3460 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
3462 DocumentationMember dm = new DocumentationMember (ecmadocs);
3464 string xp = MDocUpdater.GetXPathForMember (dm);
3465 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
3467 if (oldmember == null) {
3468 m = GetMember (type, dm);
3470 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3471 type.FullName, dm.MemberSignatures ["C#"]);
3472 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
3475 // oldmember lookup may have failed due to type parameter renames.
3477 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
3478 if (oldmember == null) {
3479 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
3480 oldmember = basefile.CreateElement ("Member");
3481 oldmember.SetAttribute ("MemberName", dm.MemberName);
3482 members.AppendChild (oldmember);
3483 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
3484 XmlElement ms = basefile.CreateElement ("MemberSignature");
3485 ms.SetAttribute ("Language", key);
3486 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
3487 oldmember.AppendChild (ms);
3489 oldmember.SetAttribute ("__monodocer-seen__", "true");
3490 Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
3495 m = GetMember (type, new DocumentationMember (oldmember));
3497 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3498 type.FullName, dm.MemberSignatures ["C#"]);
3501 oldmember.SetAttribute ("__monodocer-seen__", "true");
3503 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
3504 if (ecmadocs.Name != "Docs")
3505 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
3510 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
3519 abstract class DocumentationImporter {
3521 public abstract void ImportDocumentation (DocsNodeInfo info);
3524 class MsxdocDocumentationImporter : DocumentationImporter {
3526 XmlDocument slashdocs;
3528 public MsxdocDocumentationImporter (string file)
3530 var xml = File.ReadAllText (file);
3532 // Ensure Unix line endings
3533 xml = xml.Replace ("\r", "");
3535 slashdocs = new XmlDocument();
3536 slashdocs.LoadXml (xml);
3539 public override void ImportDocumentation (DocsNodeInfo info)
3541 XmlNode elem = GetDocs (info.Member ?? info.Type);
3546 XmlElement e = info.Node;
3548 if (elem.SelectSingleNode("summary") != null)
3549 MDocUpdater.ClearElement(e, "summary");
3550 if (elem.SelectSingleNode("remarks") != null)
3551 MDocUpdater.ClearElement(e, "remarks");
3552 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3553 MDocUpdater.ClearElement(e, "value");
3554 MDocUpdater.ClearElement(e, "returns");
3557 foreach (XmlNode child in elem.ChildNodes) {
3558 switch (child.Name) {
3561 XmlAttribute name = child.Attributes ["name"];
3564 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3566 p2.InnerXml = child.InnerXml;
3569 // Occasionally XML documentation will use <returns/> on
3570 // properties, so let's try to normalize things.
3573 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3574 v.InnerXml = child.InnerXml;
3580 case "permission": {
3581 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3584 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3586 a = e.OwnerDocument.CreateElement (child.Name);
3587 a.SetAttribute ("cref", cref.Value);
3590 a.InnerXml = child.InnerXml;
3594 XmlAttribute cref = child.Attributes ["cref"];
3597 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3599 a = e.OwnerDocument.CreateElement ("altmember");
3600 a.SetAttribute ("cref", cref.Value);
3607 if (child.NodeType == XmlNodeType.Element &&
3608 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3611 MDocUpdater.CopyNode (child, e);
3618 private XmlNode GetDocs (MemberReference member)
3620 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3621 if (slashdocsig != null)
3622 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3627 class EcmaDocumentationImporter : DocumentationImporter {
3631 public EcmaDocumentationImporter (XmlReader ecmaDocs)
3633 this.ecmadocs = ecmaDocs;
3636 public override void ImportDocumentation (DocsNodeInfo info)
3638 if (!ecmadocs.IsStartElement ("Docs")) {
3642 XmlElement e = info.Node;
3644 int depth = ecmadocs.Depth;
3645 ecmadocs.ReadStartElement ("Docs");
3646 while (ecmadocs.Read ()) {
3647 if (ecmadocs.Name == "Docs") {
3648 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3651 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3653 if (!ecmadocs.IsStartElement ())
3655 switch (ecmadocs.Name) {
3658 string name = ecmadocs.GetAttribute ("name");
3661 XmlNode doc = e.SelectSingleNode (
3662 ecmadocs.Name + "[@name='" + name + "']");
3663 string value = ecmadocs.ReadInnerXml ();
3665 doc.InnerXml = value.Replace ("\r", "");
3672 string name = ecmadocs.Name;
3673 string cref = ecmadocs.GetAttribute ("cref");
3676 XmlNode doc = e.SelectSingleNode (
3677 ecmadocs.Name + "[@cref='" + cref + "']");
3678 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3680 doc.InnerXml = value;
3682 XmlElement n = e.OwnerDocument.CreateElement (name);
3683 n.SetAttribute ("cref", cref);
3690 string name = ecmadocs.Name;
3691 string xpath = ecmadocs.Name;
3692 StringList attributes = new StringList (ecmadocs.AttributeCount);
3693 if (ecmadocs.MoveToFirstAttribute ()) {
3695 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3696 } while (ecmadocs.MoveToNextAttribute ());
3697 ecmadocs.MoveToContent ();
3699 if (attributes.Count > 0) {
3700 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3702 XmlNode doc = e.SelectSingleNode (xpath);
3703 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3705 doc.InnerXml = value;
3708 XmlElement n = e.OwnerDocument.CreateElement (name);
3710 foreach (string a in attributes) {
3711 int eq = a.IndexOf ('=');
3712 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3723 class DocumentationMember {
3724 public StringToStringMap MemberSignatures = new StringToStringMap ();
3725 public string ReturnType;
3726 public StringList Parameters;
3727 public string MemberName;
3728 public string MemberType;
3730 public DocumentationMember (XmlReader reader)
3732 MemberName = reader.GetAttribute ("MemberName");
3733 int depth = reader.Depth;
3735 StringList p = new StringList ();
3737 if (reader.NodeType != XmlNodeType.Element)
3740 bool shouldUse = true;
3742 string apistyle = reader.GetAttribute ("apistyle");
3743 shouldUse = string.IsNullOrWhiteSpace(apistyle) || apistyle == "classic"; // only use this tag if it's an 'classic' style node
3745 catch (Exception ex) {}
3746 switch (reader.Name) {
3747 case "MemberSignature":
3749 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3753 MemberType = reader.ReadElementString ();
3756 if (reader.Depth == depth + 2 && shouldUse)
3757 ReturnType = reader.ReadElementString ();
3760 if (reader.Depth == depth + 2 && shouldUse)
3761 p.Add (reader.GetAttribute ("Type"));
3764 if (reader.Depth == depth + 1)
3768 } while (go && reader.Read () && reader.Depth >= depth);
3774 public DocumentationMember (XmlNode node)
3776 MemberName = node.Attributes ["MemberName"].Value;
3777 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3778 XmlAttribute l = n.Attributes ["Language"];
3779 XmlAttribute v = n.Attributes ["Value"];
3780 XmlAttribute apistyle = n.Attributes ["apistyle"];
3781 bool shouldUse = apistyle == null || apistyle.Value == "classic";
3782 if (l != null && v != null && shouldUse)
3783 MemberSignatures [l.Value] = v.Value;
3785 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3786 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType[not(@apistyle) or @apistyle='classic']");
3788 ReturnType = rt.InnerText;
3789 XmlNodeList p = node.SelectNodes ("Parameters/Parameter[not(@apistyle) or @apistyle='classic']");
3791 Parameters = new StringList (p.Count);
3792 for (int i = 0; i < p.Count; ++i)
3793 Parameters.Add (p [i].Attributes ["Type"].Value);
3798 public class DynamicParserContext {
3799 public ReadOnlyCollection<bool> TransformFlags;
3800 public int TransformIndex;
3802 public DynamicParserContext (ICustomAttributeProvider provider)
3805 if (provider.HasCustomAttributes &&
3806 (da = (provider.CustomAttributes.Cast<CustomAttribute>()
3807 .SingleOrDefault (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))) != null) {
3808 CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
3809 ? new CustomAttributeArgument [0]
3810 : (CustomAttributeArgument[]) da.ConstructorArguments [0].Value;
3812 TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool) t.Value).ToArray());
3817 public enum MemberFormatterState {
3819 WithinGenericTypeParameters,
3822 public abstract class MemberFormatter {
3824 public virtual string Language {
3828 public string GetName (MemberReference member)
3830 return GetName (member, null);
3833 public virtual string GetName (MemberReference member, DynamicParserContext context)
3835 TypeReference type = member as TypeReference;
3837 return GetTypeName (type, context);
3838 MethodReference method = member as MethodReference;
3839 if (method != null && method.Name == ".ctor") // method.IsConstructor
3840 return GetConstructorName (method);
3842 return GetMethodName (method);
3843 PropertyReference prop = member as PropertyReference;
3845 return GetPropertyName (prop);
3846 FieldReference field = member as FieldReference;
3848 return GetFieldName (field);
3849 EventReference e = member as EventReference;
3851 return GetEventName (e);
3852 throw new NotSupportedException ("Can't handle: " +
3853 (member == null ? "null" : member.GetType().ToString()));
3856 protected virtual string GetTypeName (TypeReference type)
3858 return GetTypeName (type, null);
3861 protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
3864 throw new ArgumentNullException ("type");
3865 return _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
3868 protected virtual char[] ArrayDelimeters {
3869 get {return new char[]{'[', ']'};}
3872 protected virtual MemberFormatterState MemberFormatterState { get; set; }
3874 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3876 if (type is ArrayType) {
3877 TypeSpecification spec = type as TypeSpecification;
3878 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
3879 return AppendArrayModifiers (buf, (ArrayType) type);
3881 if (type is ByReferenceType) {
3882 return AppendRefTypeName (buf, type, context);
3884 if (type is PointerType) {
3885 return AppendPointerTypeName (buf, type, context);
3887 if (type is GenericParameter) {
3888 return AppendTypeName (buf, type, context);
3890 AppendNamespace (buf, type);
3891 GenericInstanceType genInst = type as GenericInstanceType;
3892 if (type.GenericParameters.Count == 0 &&
3893 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3894 return AppendFullTypeName (buf, type, context);
3896 return AppendGenericType (buf, type, context);
3899 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3901 string ns = DocUtils.GetNamespace (type);
3902 if (ns != null && ns.Length > 0)
3903 buf.Append (ns).Append ('.');
3907 protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3909 if (type.DeclaringType != null)
3910 AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
3911 return AppendTypeName (buf, type, context);
3914 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3916 if (context != null)
3917 context.TransformIndex++;
3918 return AppendTypeName (buf, type.Name);
3921 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3923 int n = typename.IndexOf ("`");
3925 return buf.Append (typename.Substring (0, n));
3926 return buf.Append (typename);
3929 protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
3931 buf.Append (ArrayDelimeters [0]);
3932 int rank = array.Rank;
3934 buf.Append (new string (',', rank-1));
3935 return buf.Append (ArrayDelimeters [1]);
3938 protected virtual string RefTypeModifier {
3942 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3944 TypeSpecification spec = type as TypeSpecification;
3945 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
3946 .Append (RefTypeModifier);
3949 protected virtual string PointerModifier {
3953 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3955 TypeSpecification spec = type as TypeSpecification;
3956 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
3957 .Append (PointerModifier);
3960 protected virtual char[] GenericTypeContainer {
3961 get {return new char[]{'<', '>'};}
3964 protected virtual char NestedTypeSeparator {
3968 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
3970 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3971 type is GenericInstanceType ? type.GetElementType () : type);
3972 List<TypeReference> genArgs = GetGenericArguments (type);
3975 bool insertNested = false;
3976 foreach (var decl in decls) {
3977 TypeReference declDef = decl.Resolve () ?? decl;
3979 buf.Append (NestedTypeSeparator);
3981 insertNested = true;
3982 AppendTypeName (buf, declDef, context);
3983 int ac = DocUtils.GetGenericArgumentCount (declDef);
3987 buf.Append (GenericTypeContainer [0]);
3988 var origState = MemberFormatterState;
3989 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3990 _AppendTypeName (buf, genArgs [argIdx++], context);
3991 for (int i = 1; i < c; ++i) {
3992 _AppendTypeName (buf.Append (","), genArgs [argIdx++], context);
3994 MemberFormatterState = origState;
3995 buf.Append (GenericTypeContainer [1]);
4001 protected List<TypeReference> GetGenericArguments (TypeReference type)
4003 var args = new List<TypeReference> ();
4004 GenericInstanceType inst = type as GenericInstanceType;
4006 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
4008 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
4012 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4017 protected virtual string GetConstructorName (MethodReference constructor)
4019 return constructor.Name;
4022 protected virtual string GetMethodName (MethodReference method)
4027 protected virtual string GetPropertyName (PropertyReference property)
4029 return property.Name;
4032 protected virtual string GetFieldName (FieldReference field)
4037 protected virtual string GetEventName (EventReference e)
4042 public virtual string GetDeclaration (MemberReference member)
4045 throw new ArgumentNullException ("member");
4046 TypeDefinition type = member as TypeDefinition;
4048 return GetTypeDeclaration (type);
4049 MethodDefinition method = member as MethodDefinition;
4050 if (method != null && method.IsConstructor)
4051 return GetConstructorDeclaration (method);
4053 return GetMethodDeclaration (method);
4054 PropertyDefinition prop = member as PropertyDefinition;
4056 return GetPropertyDeclaration (prop);
4057 FieldDefinition field = member as FieldDefinition;
4059 return GetFieldDeclaration (field);
4060 EventDefinition e = member as EventDefinition;
4062 return GetEventDeclaration (e);
4063 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
4066 protected virtual string GetTypeDeclaration (TypeDefinition type)
4069 throw new ArgumentNullException ("type");
4070 StringBuilder buf = new StringBuilder (type.Name.Length);
4071 _AppendTypeName (buf, type, null);
4072 AppendGenericTypeConstraints (buf, type);
4073 return buf.ToString ();
4076 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
4078 return GetConstructorName (constructor);
4081 protected virtual string GetMethodDeclaration (MethodDefinition method)
4083 if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
4084 ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
4087 // Special signature for destructors.
4088 if (method.Name == "Finalize" && method.Parameters.Count == 0)
4089 return GetFinalizerName (method);
4091 StringBuilder buf = new StringBuilder ();
4093 AppendVisibility (buf, method);
4094 if (buf.Length == 0 &&
4095 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
4098 AppendModifiers (buf, method);
4100 if (buf.Length != 0)
4102 buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
4104 AppendMethodName (buf, method);
4105 AppendGenericMethod (buf, method).Append (" ");
4106 AppendParameters (buf, method, method.Parameters);
4107 AppendGenericMethodConstraints (buf, method);
4108 return buf.ToString ();
4111 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4113 return buf.Append (method.Name);
4116 protected virtual string GetFinalizerName (MethodDefinition method)
4121 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4126 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4131 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4136 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4141 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4146 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
4148 return GetPropertyName (property);
4151 protected virtual string GetFieldDeclaration (FieldDefinition field)
4153 return GetFieldName (field);
4156 protected virtual string GetEventDeclaration (EventDefinition e)
4158 return GetEventName (e);
4162 class ILFullMemberFormatter : MemberFormatter {
4164 public override string Language {
4165 get {return "ILAsm";}
4168 protected override char NestedTypeSeparator {
4174 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4176 if (GetBuiltinType (type.FullName) != null)
4178 string ns = DocUtils.GetNamespace (type);
4179 if (ns != null && ns.Length > 0) {
4180 if (type.IsValueType)
4181 buf.Append ("valuetype ");
4183 buf.Append ("class ");
4184 buf.Append (ns).Append ('.');
4189 protected static string GetBuiltinType (string t)
4192 case "System.Byte": return "unsigned int8";
4193 case "System.SByte": return "int8";
4194 case "System.Int16": return "int16";
4195 case "System.Int32": return "int32";
4196 case "System.Int64": return "int64";
4197 case "System.IntPtr": return "native int";
4199 case "System.UInt16": return "unsigned int16";
4200 case "System.UInt32": return "unsigned int32";
4201 case "System.UInt64": return "unsigned int64";
4202 case "System.UIntPtr": return "native unsigned int";
4204 case "System.Single": return "float32";
4205 case "System.Double": return "float64";
4206 case "System.Boolean": return "bool";
4207 case "System.Char": return "char";
4208 case "System.Void": return "void";
4209 case "System.String": return "string";
4210 case "System.Object": return "object";
4215 protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
4217 return buf.Append (typename);
4220 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4222 if (type is GenericParameter) {
4223 AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
4227 string s = GetBuiltinType (type.FullName);
4229 return buf.Append (s);
4231 return base.AppendTypeName (buf, type, context);
4234 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
4236 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
4237 return buf.Append (type.Owner is TypeReference ? "!" : "!!");
4239 GenericParameterAttributes attrs = type.Attributes;
4240 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
4241 buf.Append ("class ");
4242 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
4243 buf.Append ("struct ");
4244 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
4245 buf.Append (".ctor ");
4246 IList<TypeReference> constraints = type.Constraints;
4247 MemberFormatterState = 0;
4248 if (constraints.Count > 0) {
4249 var full = new ILFullMemberFormatter ();
4250 buf.Append ("(").Append (full.GetName (constraints [0]));
4251 for (int i = 1; i < constraints.Count; ++i) {
4252 buf.Append (", ").Append (full.GetName (constraints [i]));
4256 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4258 if ((attrs & GenericParameterAttributes.Covariant) != 0)
4260 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
4265 protected override string GetTypeDeclaration (TypeDefinition type)
4267 string visibility = GetTypeVisibility (type.Attributes);
4268 if (visibility == null)
4271 StringBuilder buf = new StringBuilder ();
4273 buf.Append (".class ");
4275 buf.Append ("nested ");
4276 buf.Append (visibility).Append (" ");
4277 if (type.IsInterface)
4278 buf.Append ("interface ");
4279 if (type.IsSequentialLayout)
4280 buf.Append ("sequential ");
4281 if (type.IsAutoLayout)
4282 buf.Append ("auto ");
4283 if (type.IsAnsiClass)
4284 buf.Append ("ansi ");
4285 if (type.IsAbstract)
4286 buf.Append ("abstract ");
4287 if (type.IsSerializable)
4288 buf.Append ("serializable ");
4290 buf.Append ("sealed ");
4291 if (type.IsBeforeFieldInit)
4292 buf.Append ("beforefieldinit ");
4293 var state = MemberFormatterState;
4294 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4295 buf.Append (GetName (type));
4296 MemberFormatterState = state;
4297 var full = new ILFullMemberFormatter ();
4298 if (type.BaseType != null) {
4299 buf.Append (" extends ");
4300 if (type.BaseType.FullName == "System.Object")
4301 buf.Append ("System.Object");
4303 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
4306 foreach (var name in type.Interfaces
4307 .Select (i => full.GetName (i))
4308 .OrderBy (n => n)) {
4310 buf.Append (" implements ");
4319 return buf.ToString ();
4322 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4324 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4325 type is GenericInstanceType ? type.GetElementType () : type);
4327 foreach (var decl in decls) {
4328 TypeReference declDef = decl.Resolve () ?? decl;
4330 buf.Append (NestedTypeSeparator);
4333 AppendTypeName (buf, declDef, context);
4337 foreach (TypeReference arg in GetGenericArguments (type)) {
4341 _AppendTypeName (buf, arg, context);
4347 static string GetTypeVisibility (TypeAttributes ta)
4349 switch (ta & TypeAttributes.VisibilityMask) {
4350 case TypeAttributes.Public:
4351 case TypeAttributes.NestedPublic:
4354 case TypeAttributes.NestedFamily:
4355 case TypeAttributes.NestedFamORAssem:
4363 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4365 return GetMethodDeclaration (constructor);
4368 protected override string GetMethodDeclaration (MethodDefinition method)
4370 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
4373 var buf = new StringBuilder ();
4374 buf.Append (".method ");
4375 AppendVisibility (buf, method);
4376 if (method.IsStatic)
4377 buf.Append ("static ");
4378 if (method.IsHideBySig)
4379 buf.Append ("hidebysig ");
4380 if (method.IsPInvokeImpl) {
4381 var info = method.PInvokeInfo;
4382 buf.Append ("pinvokeimpl (\"")
4383 .Append (info.Module.Name)
4384 .Append ("\" as \"")
4385 .Append (info.EntryPoint)
4387 if (info.IsCharSetAuto)
4388 buf.Append (" auto");
4389 if (info.IsCharSetUnicode)
4390 buf.Append (" unicode");
4391 if (info.IsCharSetAnsi)
4392 buf.Append (" ansi");
4393 if (info.IsCallConvCdecl)
4394 buf.Append (" cdecl");
4395 if (info.IsCallConvStdCall)
4396 buf.Append (" stdcall");
4397 if (info.IsCallConvWinapi)
4398 buf.Append (" winapi");
4399 if (info.IsCallConvThiscall)
4400 buf.Append (" thiscall");
4401 if (info.SupportsLastError)
4402 buf.Append (" lasterr");
4405 if (method.IsSpecialName)
4406 buf.Append ("specialname ");
4407 if (method.IsRuntimeSpecialName)
4408 buf.Append ("rtspecialname ");
4409 if (method.IsNewSlot)
4410 buf.Append ("newslot ");
4411 if (method.IsVirtual)
4412 buf.Append ("virtual ");
4413 if (!method.IsStatic)
4414 buf.Append ("instance ");
4415 _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
4417 .Append (method.Name);
4418 if (method.IsGenericMethod ()) {
4419 var state = MemberFormatterState;
4420 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4421 IList<GenericParameter> args = method.GenericParameters;
4422 if (args.Count > 0) {
4424 _AppendTypeName (buf, args [0], null);
4425 for (int i = 1; i < args.Count; ++i)
4426 _AppendTypeName (buf.Append (", "), args [i], null);
4429 MemberFormatterState = state;
4434 for (int i = 0; i < method.Parameters.Count; ++i) {
4438 _AppendTypeName (buf, method.Parameters [i].ParameterType, new DynamicParserContext (method.Parameters [i]));
4440 buf.Append (method.Parameters [i].Name);
4444 buf.Append (" cil");
4445 if (method.IsRuntime)
4446 buf.Append (" runtime");
4447 if (method.IsManaged)
4448 buf.Append (" managed");
4450 return buf.ToString ();
4453 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4455 if (DocUtils.IsExplicitlyImplemented (method)) {
4456 TypeReference iface;
4457 MethodReference ifaceMethod;
4458 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4459 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4461 .Append (ifaceMethod.Name);
4463 return base.AppendMethodName (buf, method);
4466 protected override string RefTypeModifier {
4470 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4472 if (method.IsPublic)
4473 return buf.Append ("public ");
4474 if (method.IsFamilyAndAssembly)
4475 return buf.Append ("familyandassembly");
4476 if (method.IsFamilyOrAssembly)
4477 return buf.Append ("familyorassembly");
4478 if (method.IsFamily)
4479 return buf.Append ("family");
4483 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4485 string modifiers = String.Empty;
4486 if (method.IsStatic) modifiers += " static";
4487 if (method.IsVirtual && !method.IsAbstract) {
4488 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4489 else modifiers += " override";
4491 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4492 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4493 if (method.IsFinal) modifiers += " sealed";
4494 if (modifiers == " virtual sealed") modifiers = "";
4496 return buf.Append (modifiers);
4499 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4501 if (method.IsGenericMethod ()) {
4502 IList<GenericParameter> args = method.GenericParameters;
4503 if (args.Count > 0) {
4505 buf.Append (args [0].Name);
4506 for (int i = 1; i < args.Count; ++i)
4507 buf.Append (",").Append (args [i].Name);
4514 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4516 return AppendParameters (buf, method, parameters, '(', ')');
4519 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4523 if (parameters.Count > 0) {
4524 if (DocUtils.IsExtensionMethod (method))
4525 buf.Append ("this ");
4526 AppendParameter (buf, parameters [0]);
4527 for (int i = 1; i < parameters.Count; ++i) {
4529 AppendParameter (buf, parameters [i]);
4533 return buf.Append (end);
4536 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4538 if (parameter.ParameterType is ByReferenceType) {
4539 if (parameter.IsOut)
4540 buf.Append ("out ");
4542 buf.Append ("ref ");
4544 buf.Append (GetName (parameter.ParameterType)).Append (" ");
4545 return buf.Append (parameter.Name);
4548 protected override string GetPropertyDeclaration (PropertyDefinition property)
4550 MethodDefinition gm = null, sm = null;
4552 string get_visible = null;
4553 if ((gm = property.GetMethod) != null &&
4554 (DocUtils.IsExplicitlyImplemented (gm) ||
4555 (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
4556 get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
4557 string set_visible = null;
4558 if ((sm = property.SetMethod) != null &&
4559 (DocUtils.IsExplicitlyImplemented (sm) ||
4560 (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
4561 set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
4563 if ((set_visible == null) && (get_visible == null))
4566 StringBuilder buf = new StringBuilder ()
4567 .Append (".property ");
4568 if (!(gm ?? sm).IsStatic)
4569 buf.Append ("instance ");
4570 _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
4571 buf.Append (' ').Append (property.Name);
4572 if (!property.HasParameters || property.Parameters.Count == 0)
4573 return buf.ToString ();
4577 foreach (ParameterDefinition p in property.Parameters) {
4581 _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
4585 return buf.ToString ();
4588 protected override string GetFieldDeclaration (FieldDefinition field)
4590 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4591 if (declType.IsEnum && field.Name == "value__")
4592 return null; // This member of enums aren't documented.
4594 StringBuilder buf = new StringBuilder ();
4595 AppendFieldVisibility (buf, field);
4596 if (buf.Length == 0)
4599 buf.Insert (0, ".field ");
4602 buf.Append ("static ");
4603 if (field.IsInitOnly)
4604 buf.Append ("initonly ");
4605 if (field.IsLiteral)
4606 buf.Append ("literal ");
4607 _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
4608 buf.Append (' ').Append (field.Name);
4609 AppendFieldValue (buf, field);
4611 return buf.ToString ();
4614 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4617 return buf.Append ("public ");
4618 if (field.IsFamilyAndAssembly)
4619 return buf.Append ("familyandassembly ");
4620 if (field.IsFamilyOrAssembly)
4621 return buf.Append ("familyorassembly ");
4623 return buf.Append ("family ");
4627 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4629 // enums have a value__ field, which we ignore
4630 if (field.DeclaringType.IsGenericType ())
4632 if (field.HasConstant && field.IsLiteral) {
4635 val = field.Constant;
4640 buf.Append (" = ").Append ("null");
4641 else if (val is Enum)
4643 .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4645 .Append (val.ToString ())
4647 else if (val is IFormattable) {
4648 string value = ((IFormattable)val).ToString();
4651 buf.Append ("\"" + value + "\"");
4653 buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4662 protected override string GetEventDeclaration (EventDefinition e)
4664 StringBuilder buf = new StringBuilder ();
4665 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4670 buf.Append (".event ")
4671 .Append (GetName (e.EventType))
4675 return buf.ToString ();
4679 class ILMemberFormatter : ILFullMemberFormatter {
4680 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4686 class ILNativeTypeMemberFormatter : ILFullMemberFormatter {
4687 protected static string _GetBuiltinType (string t)
4689 //string moddedType = base.GetBuiltinType (t);
4691 //return moddedType;
4695 class CSharpNativeTypeMemberFormatter : CSharpFullMemberFormatter {
4696 protected override string GetCSharpType (string t) {
4697 string moddedType = base.GetCSharpType (t);
4699 switch (moddedType) {
4700 case "int": return "nint";
4705 case "System.Drawing.SizeF":
4706 return "CoreGraphics.CGSize";
4707 case "System.Drawing.PointF":
4708 return "CoreGraphics.CGPoint";
4709 case "System.Drawing.RectangleF":
4710 return "CoreGraphics.CGPoint";
4716 class CSharpFullMemberFormatter : MemberFormatter {
4718 public override string Language {
4722 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4725 string ns = DocUtils.GetNamespace (type);
4726 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
4727 buf.Append (ns).Append ('.');
4731 protected virtual string GetCSharpType (string t)
4734 case "System.Byte": return "byte";
4735 case "System.SByte": return "sbyte";
4736 case "System.Int16": return "short";
4737 case "System.Int32": return "int";
4738 case "System.Int64": return "long";
4740 case "System.UInt16": return "ushort";
4741 case "System.UInt32": return "uint";
4742 case "System.UInt64": return "ulong";
4744 case "System.Single": return "float";
4745 case "System.Double": return "double";
4746 case "System.Decimal": return "decimal";
4747 case "System.Boolean": return "bool";
4748 case "System.Char": return "char";
4749 case "System.Void": return "void";
4750 case "System.String": return "string";
4751 case "System.Object": return "object";
4756 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4758 if (context != null && context.TransformFlags != null &&
4759 (context.TransformFlags.Count == 0 || context.TransformFlags [context.TransformIndex])) {
4760 context.TransformIndex++;
4761 return buf.Append ("dynamic");
4764 if (type is GenericParameter)
4765 return AppendGenericParameterConstraints (buf, (GenericParameter) type, context).Append (type.Name);
4766 string t = type.FullName;
4767 if (!t.StartsWith ("System.")) {
4768 return base.AppendTypeName (buf, type, context);
4771 string s = GetCSharpType (t);
4773 if (context != null)
4774 context.TransformIndex++;
4775 return buf.Append (s);
4778 return base.AppendTypeName (buf, type, context);
4781 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
4783 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
4785 GenericParameterAttributes attrs = type.Attributes;
4786 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
4787 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
4791 buf.Append ("out ");
4795 protected override string GetTypeDeclaration (TypeDefinition type)
4797 string visibility = GetTypeVisibility (type.Attributes);
4798 if (visibility == null)
4801 StringBuilder buf = new StringBuilder ();
4803 buf.Append (visibility);
4806 MemberFormatter full = new CSharpFullMemberFormatter ();
4808 if (DocUtils.IsDelegate (type)) {
4809 buf.Append("delegate ");
4810 MethodDefinition invoke = type.GetMethod ("Invoke");
4811 buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
4812 buf.Append (GetName (type));
4813 AppendParameters (buf, invoke, invoke.Parameters);
4814 AppendGenericTypeConstraints (buf, type);
4817 return buf.ToString();
4820 if (type.IsAbstract && !type.IsInterface)
4821 buf.Append("abstract ");
4822 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
4823 buf.Append("sealed ");
4824 buf.Replace ("abstract sealed", "static");
4826 buf.Append (GetTypeKind (type));
4828 buf.Append (GetCSharpType (type.FullName) == null
4833 TypeReference basetype = type.BaseType;
4834 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
4837 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
4838 .Select (iface => full.GetName (iface))
4842 if (basetype != null || interface_names.Count > 0)
4845 if (basetype != null) {
4846 buf.Append (full.GetName (basetype));
4847 if (interface_names.Count > 0)
4851 for (int i = 0; i < interface_names.Count; i++){
4854 buf.Append (interface_names [i]);
4856 AppendGenericTypeConstraints (buf, type);
4859 return buf.ToString ();
4862 static string GetTypeKind (TypeDefinition t)
4868 if (t.IsClass || t.FullName == "System.Enum")
4872 throw new ArgumentException(t.FullName);
4875 static string GetTypeVisibility (TypeAttributes ta)
4877 switch (ta & TypeAttributes.VisibilityMask) {
4878 case TypeAttributes.Public:
4879 case TypeAttributes.NestedPublic:
4882 case TypeAttributes.NestedFamily:
4883 case TypeAttributes.NestedFamORAssem:
4891 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4893 if (type.GenericParameters.Count == 0)
4895 return AppendConstraints (buf, type.GenericParameters);
4898 private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
4900 foreach (GenericParameter genArg in genArgs) {
4901 GenericParameterAttributes attrs = genArg.Attributes;
4902 IList<TypeReference> constraints = genArg.Constraints;
4903 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
4906 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
4907 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
4908 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
4911 if (!isref && !isvt && !isnew && constraints.Count == 0)
4913 buf.Append (" where ").Append (genArg.Name).Append (" : ");
4915 buf.Append ("class");
4919 buf.Append ("struct");
4922 if (constraints.Count > 0 && !isvt) {
4925 buf.Append (GetTypeName (constraints [0]));
4926 for (int i = 1; i < constraints.Count; ++i)
4927 buf.Append (", ").Append (GetTypeName (constraints [i]));
4929 if (isnew && !isvt) {
4932 buf.Append ("new()");
4938 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4940 StringBuilder buf = new StringBuilder ();
4941 AppendVisibility (buf, constructor);
4942 if (buf.Length == 0)
4946 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
4947 AppendParameters (buf, constructor, constructor.Parameters);
4950 return buf.ToString ();
4953 protected override string GetMethodDeclaration (MethodDefinition method)
4955 string decl = base.GetMethodDeclaration (method);
4961 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4963 if (DocUtils.IsExplicitlyImplemented (method)) {
4964 TypeReference iface;
4965 MethodReference ifaceMethod;
4966 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4967 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4969 .Append (ifaceMethod.Name);
4971 return base.AppendMethodName (buf, method);
4974 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4976 if (method.GenericParameters.Count == 0)
4978 return AppendConstraints (buf, method.GenericParameters);
4981 protected override string RefTypeModifier {
4985 protected override string GetFinalizerName (MethodDefinition method)
4987 return "~" + method.DeclaringType.Name + " ()";
4990 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4994 if (method.IsPublic)
4995 return buf.Append ("public");
4996 if (method.IsFamily || method.IsFamilyOrAssembly)
4997 return buf.Append ("protected");
5001 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
5003 string modifiers = String.Empty;
5004 if (method.IsStatic) modifiers += " static";
5005 if (method.IsVirtual && !method.IsAbstract) {
5006 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
5007 else modifiers += " override";
5009 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
5010 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
5011 if (method.IsFinal) modifiers += " sealed";
5012 if (modifiers == " virtual sealed") modifiers = "";
5014 return buf.Append (modifiers);
5017 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
5019 if (method.IsGenericMethod ()) {
5020 IList<GenericParameter> args = method.GenericParameters;
5021 if (args.Count > 0) {
5023 buf.Append (args [0].Name);
5024 for (int i = 1; i < args.Count; ++i)
5025 buf.Append (",").Append (args [i].Name);
5032 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
5034 return AppendParameters (buf, method, parameters, '(', ')');
5037 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
5041 if (parameters.Count > 0) {
5042 if (DocUtils.IsExtensionMethod (method))
5043 buf.Append ("this ");
5044 AppendParameter (buf, parameters [0]);
5045 for (int i = 1; i < parameters.Count; ++i) {
5047 AppendParameter (buf, parameters [i]);
5051 return buf.Append (end);
5054 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
5056 if (parameter.ParameterType is ByReferenceType) {
5057 if (parameter.IsOut)
5058 buf.Append ("out ");
5060 buf.Append ("ref ");
5062 buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
5063 buf.Append (parameter.Name);
5064 if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant) {
5065 buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
5070 protected override string GetPropertyDeclaration (PropertyDefinition property)
5072 MethodDefinition method;
5074 string get_visible = null;
5075 if ((method = property.GetMethod) != null &&
5076 (DocUtils.IsExplicitlyImplemented (method) ||
5077 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5078 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5079 string set_visible = null;
5080 if ((method = property.SetMethod) != null &&
5081 (DocUtils.IsExplicitlyImplemented (method) ||
5082 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5083 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5085 if ((set_visible == null) && (get_visible == null))
5089 StringBuilder buf = new StringBuilder ();
5090 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
5091 buf.Append (visibility = get_visible);
5092 else if (set_visible != null && get_visible == null)
5093 buf.Append (visibility = set_visible);
5095 buf.Append (visibility = "public");
5097 // Pick an accessor to use for static/virtual/override/etc. checks.
5098 method = property.SetMethod;
5100 method = property.GetMethod;
5102 string modifiers = String.Empty;
5103 if (method.IsStatic) modifiers += " static";
5104 if (method.IsVirtual && !method.IsAbstract) {
5105 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
5106 modifiers += " virtual";
5108 modifiers += " override";
5110 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
5111 if (method.IsAbstract && !declDef.IsInterface)
5112 modifiers += " abstract";
5114 modifiers += " sealed";
5115 if (modifiers == " virtual sealed")
5117 buf.Append (modifiers).Append (' ');
5119 buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
5121 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
5122 string name = property.Name;
5123 foreach (MemberReference mi in defs) {
5124 if (mi == property) {
5129 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
5131 if (property.Parameters.Count != 0) {
5132 AppendParameters (buf, method, property.Parameters, '[', ']');
5136 if (get_visible != null) {
5137 if (get_visible != visibility)
5138 buf.Append (' ').Append (get_visible);
5139 buf.Append (" get;");
5141 if (set_visible != null) {
5142 if (set_visible != visibility)
5143 buf.Append (' ').Append (set_visible);
5144 buf.Append (" set;");
5148 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
5151 protected override string GetFieldDeclaration (FieldDefinition field)
5153 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
5154 if (declType.IsEnum && field.Name == "value__")
5155 return null; // This member of enums aren't documented.
5157 StringBuilder buf = new StringBuilder ();
5158 AppendFieldVisibility (buf, field);
5159 if (buf.Length == 0)
5162 if (declType.IsEnum)
5165 if (field.IsStatic && !field.IsLiteral)
5166 buf.Append (" static");
5167 if (field.IsInitOnly)
5168 buf.Append (" readonly");
5169 if (field.IsLiteral)
5170 buf.Append (" const");
5172 buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
5173 buf.Append (field.Name);
5174 AppendFieldValue (buf, field);
5177 return buf.ToString ();
5180 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
5183 return buf.Append ("public");
5184 if (field.IsFamily || field.IsFamilyOrAssembly)
5185 return buf.Append ("protected");
5189 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
5191 // enums have a value__ field, which we ignore
5192 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
5193 field.DeclaringType.IsGenericType ())
5195 if (field.HasConstant && field.IsLiteral) {
5198 val = field.Constant;
5203 buf.Append (" = ").Append ("null");
5204 else if (val is Enum)
5205 buf.Append (" = ").Append (val.ToString ());
5206 else if (val is IFormattable) {
5207 string value = ((IFormattable)val).ToString();
5209 value = "\"" + value + "\"";
5210 buf.Append (" = ").Append (value);
5216 protected override string GetEventDeclaration (EventDefinition e)
5218 StringBuilder buf = new StringBuilder ();
5219 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
5223 AppendModifiers (buf, e.AddMethod);
5225 buf.Append (" event ");
5226 buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters [0]))).Append (' ');
5227 buf.Append (e.Name).Append (';');
5229 return buf.ToString ();
5233 class CSharpMemberFormatter : CSharpFullMemberFormatter {
5234 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5240 class DocTypeFullMemberFormatter : MemberFormatter {
5241 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
5243 protected override char NestedTypeSeparator {
5248 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
5249 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5255 class SlashDocMemberFormatter : MemberFormatter {
5257 protected override char[] GenericTypeContainer {
5258 get {return new char[]{'{', '}'};}
5261 private bool AddTypeCount = true;
5263 private TypeReference genDeclType;
5264 private MethodReference genDeclMethod;
5266 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
5268 if (type is GenericParameter) {
5270 if (genDeclType != null) {
5271 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
5272 for (int i = 0; i < genArgs.Count; ++i) {
5273 if (genArgs [i].Name == type.Name) {
5274 buf.Append ('`').Append (i);
5279 if (genDeclMethod != null) {
5280 IList<GenericParameter> genArgs = null;
5281 if (genDeclMethod.IsGenericMethod ()) {
5282 genArgs = genDeclMethod.GenericParameters;
5283 for (int i = 0; i < genArgs.Count; ++i) {
5284 if (genArgs [i].Name == type.Name) {
5285 buf.Append ("``").Append (i);
5291 if (genDeclType == null && genDeclMethod == null) {
5292 // Probably from within an explicitly implemented interface member,
5293 // where CSC uses parameter names instead of indices (why?), e.g.
5294 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
5295 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
5296 buf.Append (type.Name);
5298 if (buf.Length == l) {
5299 throw new Exception (string.Format (
5300 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
5301 type.Name, genDeclType, genDeclMethod));
5305 base.AppendTypeName (buf, type, context);
5307 int numArgs = type.GenericParameters.Count;
5308 if (type.DeclaringType != null)
5309 numArgs -= type.GenericParameters.Count;
5311 buf.Append ('`').Append (numArgs);
5318 protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
5320 buf.Append (ArrayDelimeters [0]);
5321 int rank = array.Rank;
5324 for (int i = 1; i < rank; ++i) {
5328 return buf.Append (ArrayDelimeters [1]);
5331 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5334 base.AppendGenericType (buf, type, context);
5336 AppendType (buf, type, context);
5340 private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5342 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
5343 bool insertNested = false;
5344 int prevParamCount = 0;
5345 foreach (var decl in decls) {
5347 buf.Append (NestedTypeSeparator);
5348 insertNested = true;
5349 base.AppendTypeName (buf, decl, context);
5350 int argCount = DocUtils.GetGenericArgumentCount (decl);
5351 int numArgs = argCount - prevParamCount;
5352 prevParamCount = argCount;
5354 buf.Append ('`').Append (numArgs);
5359 public override string GetDeclaration (MemberReference member)
5361 TypeReference r = member as TypeReference;
5363 return "T:" + GetTypeName (r);
5365 return base.GetDeclaration (member);
5368 protected override string GetConstructorName (MethodReference constructor)
5370 return GetMethodDefinitionName (constructor, "#ctor");
5373 protected override string GetMethodName (MethodReference method)
5376 MethodDefinition methodDef = method as MethodDefinition;
5377 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
5380 TypeReference iface;
5381 MethodReference ifaceMethod;
5382 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
5383 AddTypeCount = false;
5384 name = GetTypeName (iface) + "." + ifaceMethod.Name;
5385 AddTypeCount = true;
5387 return GetMethodDefinitionName (method, name);
5390 private string GetMethodDefinitionName (MethodReference method, string name)
5392 StringBuilder buf = new StringBuilder ();
5393 buf.Append (GetTypeName (method.DeclaringType));
5395 buf.Append (name.Replace (".", "#"));
5396 if (method.IsGenericMethod ()) {
5397 IList<GenericParameter> genArgs = method.GenericParameters;
5398 if (genArgs.Count > 0)
5399 buf.Append ("``").Append (genArgs.Count);
5401 IList<ParameterDefinition> parameters = method.Parameters;
5403 genDeclType = method.DeclaringType;
5404 genDeclMethod = method;
5405 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
5409 genDeclMethod = null;
5411 return buf.ToString ();
5414 private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
5416 if (parameters.Count == 0)
5421 AppendParameter (buf, genArgs, parameters [0]);
5422 for (int i = 1; i < parameters.Count; ++i) {
5424 AppendParameter (buf, genArgs, parameters [i]);
5427 return buf.Append (')');
5430 private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
5432 AddTypeCount = false;
5433 buf.Append (GetTypeName (parameter.ParameterType));
5434 AddTypeCount = true;
5438 protected override string GetPropertyName (PropertyReference property)
5442 PropertyDefinition propertyDef = property as PropertyDefinition;
5443 MethodDefinition method = null;
5444 if (propertyDef != null)
5445 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
5446 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
5447 name = property.Name;
5449 TypeReference iface;
5450 MethodReference ifaceMethod;
5451 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5452 AddTypeCount = false;
5453 name = string.Join ("#", new string[]{
5454 GetTypeName (iface).Replace (".", "#"),
5455 DocUtils.GetMember (property.Name)
5457 AddTypeCount = true;
5460 StringBuilder buf = new StringBuilder ();
5461 buf.Append (GetName (property.DeclaringType));
5464 IList<ParameterDefinition> parameters = property.Parameters;
5465 if (parameters.Count > 0) {
5466 genDeclType = property.DeclaringType;
5468 IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
5469 AppendParameter (buf, genArgs, parameters [0]);
5470 for (int i = 1; i < parameters.Count; ++i) {
5472 AppendParameter (buf, genArgs, parameters [i]);
5477 return buf.ToString ();
5480 protected override string GetFieldName (FieldReference field)
5482 return string.Format ("{0}.{1}",
5483 GetName (field.DeclaringType), field.Name);
5486 protected override string GetEventName (EventReference e)
5488 return string.Format ("{0}.{1}",
5489 GetName (e.DeclaringType), e.Name);
5492 protected override string GetTypeDeclaration (TypeDefinition type)
5494 string name = GetName (type);
5500 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5502 string name = GetName (constructor);
5508 protected override string GetMethodDeclaration (MethodDefinition method)
5510 string name = GetName (method);
5513 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
5514 genDeclType = method.DeclaringType;
5515 genDeclMethod = method;
5516 name += "~" + GetName (method.ReturnType);
5518 genDeclMethod = null;
5523 protected override string GetPropertyDeclaration (PropertyDefinition property)
5525 string name = GetName (property);
5531 protected override string GetFieldDeclaration (FieldDefinition field)
5533 string name = GetName (field);
5539 protected override string GetEventDeclaration (EventDefinition e)
5541 string name = GetName (e);
5548 class FileNameMemberFormatter : SlashDocMemberFormatter {
5549 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5554 protected override char NestedTypeSeparator {