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 && !typename.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (t)) {
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;
170 public static bool HasDroppedNamespace(TypeDefinition forType)
172 return HasDroppedNamespace(forType.Module);
175 public static bool HasDroppedNamespace(MemberReference forMember)
177 return HasDroppedNamespace(forMember.Module);
180 public static bool HasDroppedNamespace(AssemblyDefinition forAssembly)
182 return HasDroppedNamespace(forAssembly.MainModule);
185 public static bool HasDroppedNamespace(ModuleDefinition forModule)
187 return !string.IsNullOrWhiteSpace (droppedNamespace) && droppedAssemblies.Any(da => da == forModule.Name);
191 static List<string> droppedAssemblies = new List<string>();
193 public string PreserveTag { get; set; }
194 public static MDocUpdater Instance { get; private set; }
195 public static bool SwitchingToMagicTypes { get; private set; }
197 public override void Run (IEnumerable<string> args)
200 show_exceptions = DebugOutput;
201 var types = new List<string> ();
202 var p = new OptionSet () {
204 "Delete removed members from the XML files.",
205 v => delete = v != null },
207 "Document potential exceptions that members can generate. {SOURCES} " +
208 "is a comma-separated list of:\n" +
209 " asm Method calls in same assembly\n" +
210 " depasm Method calls in dependent assemblies\n" +
211 " all Record all possible exceptions\n" +
212 " added Modifier; only create <exception/>s\n" +
213 " for NEW types/members\n" +
214 "If nothing is specified, then only exceptions from the member will " +
216 v => exceptions = ParseExceptionLocations (v) },
218 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
221 case "ignore-missing-types":
222 ignore_missing_types = true;
224 case "no-assembly-versions":
225 no_assembly_versions = true;
228 throw new Exception ("Unsupported flag `" + v + "'.");
231 { "fignore-missing-types",
232 "Do not report an error if a --type=TYPE type\nwas not found.",
233 v => ignore_missing_types = v != null },
234 { "fno-assembly-versions",
235 "Do not generate //AssemblyVersion elements.",
236 v => no_assembly_versions = v != null },
238 "Import documentation from {FILE}.",
239 v => AddImporter (v) },
241 "Check for assembly references in {DIRECTORY}.",
242 v => assemblyResolver.AddSearchDirectory (v) },
244 "Ignored for compatibility with update-ecma-xml.",
247 "Root {DIRECTORY} to generate/update documentation.",
250 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
251 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
252 v => assemblyResolver.AddSearchDirectory (Path.GetDirectoryName (v)) },
254 "Manually specify the assembly {VERSION} that new members were added in.",
257 "Only update documentation for {TYPE}.",
258 v => types.Add (v) },
260 "When processing assembly {ASSEMBLY}, strip off leading namespace {PREFIX}:\n" +
261 " e.g. --dropns ASSEMBLY=PREFIX",
263 var parts = v.Split ('=');
264 if (parts.Length != 2) { Console.Error.WriteLine ("Invalid dropns input"); return; }
265 var assembly = Path.GetFileName (parts [0].Trim ());
266 var prefix = parts [1].Trim();
267 droppedAssemblies.Add (assembly);
268 droppedNamespace = prefix;
271 "If the new assembly is switching to 'magic types', then this switch should be defined.",
272 v => SwitchingToMagicTypes = true },
274 "Do not delete members that don't exist in the assembly, but rather mark them as preserved.",
275 v => PreserveTag = "true" },
277 var assemblies = Parse (p, args, "update",
278 "[OPTIONS]+ ASSEMBLIES",
279 "Create or update documentation from ASSEMBLIES.");
280 if (assemblies == null)
282 if (assemblies.Count == 0)
283 Error ("No assemblies specified.");
285 foreach (var dir in assemblies
286 .Where (a => a.Contains (Path.DirectorySeparatorChar))
287 .Select (a => Path.GetDirectoryName (a)))
288 assemblyResolver.AddSearchDirectory (dir);
290 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
293 throw new InvalidOperationException("The --out option is required.");
295 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
297 // Store types that have been forwarded to avoid duplicate generation
298 GatherForwardedTypes ();
300 docEnum = docEnum ?? new DocumentationEnumerator ();
302 // PERFORM THE UPDATES
304 if (types.Count > 0) {
306 DoUpdateTypes (srcPath, types, srcPath);
309 else if (opts.@namespace != null)
310 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
311 Path.Combine (dest_dir, opts.@namespace));
314 DoUpdateAssemblies (srcPath, srcPath);
316 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
318 public static bool IsInAssemblies(string name) {
319 var query = Instance.assemblies.Where (a => a.MainModule.Name == name).ToArray ();
320 return query.Length > 0;
322 void AddImporter (string path)
325 XmlReader r = new XmlTextReader (path);
327 while (r.NodeType != XmlNodeType.Element) {
329 Error ("Unable to read XML file: {0}.", path);
331 if (r.LocalName == "doc") {
332 importers.Add (new MsxdocDocumentationImporter (path));
334 else if (r.LocalName == "Libraries") {
335 var ecmadocs = new XmlTextReader (path);
336 docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
337 importers.Add (new EcmaDocumentationImporter (ecmadocs));
340 Error ("Unsupported XML format within {0}.", path);
343 } catch (Exception e) {
344 Environment.ExitCode = 1;
345 Error ("Could not load XML file: {0}.", e.Message);
349 void GatherForwardedTypes ()
351 foreach (var asm in assemblies)
352 foreach (var type in asm.MainModule.ExportedTypes.Where (t => t.IsForwarder).Select (t => t.FullName))
353 forwardedTypes.Add (type);
356 static ExceptionLocations ParseExceptionLocations (string s)
358 ExceptionLocations loc = ExceptionLocations.Member;
361 foreach (var type in s.Split (',')) {
363 case "added": loc |= ExceptionLocations.AddedMembers; break;
364 case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
365 case "asm": loc |= ExceptionLocations.Assembly; break;
366 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
367 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
373 internal void Warning (string format, params object[] args)
375 Message (TraceLevel.Warning, "mdoc: " + format, args);
378 private AssemblyDefinition LoadAssembly (string name)
380 AssemblyDefinition assembly = null;
382 assembly = AssemblyDefinition.ReadAssembly (name, new ReaderParameters { AssemblyResolver = assemblyResolver });
383 } catch (System.IO.FileNotFoundException) { }
385 if (assembly == null)
386 throw new InvalidOperationException("Assembly " + name + " not found.");
391 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
392 OrderTypeAttributes (element);
393 XmlTextWriter writer = new XmlTextWriter(output);
394 writer.Formatting = Formatting.Indented;
395 writer.Indentation = 2;
396 writer.IndentChar = ' ';
397 element.WriteTo(writer);
401 private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
403 Action<string> creator = file => {
404 using (var writer = OpenWrite (file, mode))
408 MdocFile.UpdateFile (filename, creator);
411 private static void OrderTypeAttributes (XmlElement e)
413 foreach (XmlElement type in e.SelectNodes ("//Type")) {
414 OrderTypeAttributes (type.Attributes);
418 static readonly string[] TypeAttributeOrder = {
419 "Name", "FullName", "FullNameSP", "Maintainer"
422 private static void OrderTypeAttributes (XmlAttributeCollection c)
424 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
425 for (int i = 0; i < c.Count; ++i) {
426 XmlAttribute a = c [i];
427 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
428 if (a.Name == TypeAttributeOrder [j]) {
434 for (int i = attrs.Length-1; i >= 0; --i) {
435 XmlAttribute n = attrs [i];
438 XmlAttribute r = null;
439 for (int j = i+1; j < attrs.Length; ++j) {
440 if (attrs [j] != null) {
448 c.InsertBefore (n, r);
452 private XmlDocument CreateIndexStub()
454 XmlDocument index = new XmlDocument();
456 XmlElement index_root = index.CreateElement("Overview");
457 index.AppendChild(index_root);
459 if (assemblies.Count == 0)
460 throw new Exception ("No assembly");
462 XmlElement index_assemblies = index.CreateElement("Assemblies");
463 index_root.AppendChild(index_assemblies);
465 XmlElement index_remarks = index.CreateElement("Remarks");
466 index_remarks.InnerText = "To be added.";
467 index_root.AppendChild(index_remarks);
469 XmlElement index_copyright = index.CreateElement("Copyright");
470 index_copyright.InnerText = "To be added.";
471 index_root.AppendChild(index_copyright);
473 XmlElement index_types = index.CreateElement("Types");
474 index_root.AppendChild(index_types);
479 private static void WriteNamespaceStub(string ns, string outdir) {
480 XmlDocument index = new XmlDocument();
482 XmlElement index_root = index.CreateElement("Namespace");
483 index.AppendChild(index_root);
485 index_root.SetAttribute("Name", ns);
487 XmlElement index_docs = index.CreateElement("Docs");
488 index_root.AppendChild(index_docs);
490 XmlElement index_summary = index.CreateElement("summary");
491 index_summary.InnerText = "To be added.";
492 index_docs.AppendChild(index_summary);
494 XmlElement index_remarks = index.CreateElement("remarks");
495 index_remarks.InnerText = "To be added.";
496 index_docs.AppendChild(index_remarks);
498 WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
499 writer => WriteXml (index.DocumentElement, writer));
502 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
504 var index = CreateIndexForTypes (dest);
506 var found = new HashSet<string> ();
507 foreach (AssemblyDefinition assembly in assemblies) {
508 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames)) {
509 string relpath = DoUpdateType (type, basepath, dest);
513 found.Add (type.FullName);
518 index.Add (assembly);
526 if (ignore_missing_types)
529 var notFound = from n in typenames where !found.Contains (n) select n;
531 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
534 class IndexForTypes {
540 XmlElement index_types;
541 XmlElement index_assemblies;
543 public IndexForTypes (MDocUpdater app, string indexFile, XmlDocument index)
546 this.indexFile = indexFile;
549 index_types = WriteElement (index.DocumentElement, "Types");
550 index_assemblies = WriteElement (index.DocumentElement, "Assemblies");
553 public void Add (AssemblyDefinition assembly)
555 if (index_assemblies.SelectSingleNode ("Assembly[@Name='" + assembly.Name.Name + "']") != null)
558 app.AddIndexAssembly (assembly, index_assemblies);
561 public void Add (TypeDefinition type)
563 app.AddIndexType (type, index_types);
568 SortIndexEntries (index_types);
569 WriteFile (indexFile, FileMode.Create,
570 writer => WriteXml (index.DocumentElement, writer));
574 IndexForTypes CreateIndexForTypes (string dest)
576 string indexFile = Path.Combine (dest, "index.xml");
577 if (File.Exists (indexFile))
579 return new IndexForTypes (this, indexFile, CreateIndexStub ());
582 /// <summary>Constructs the presumed path to the type's documentation file</summary>
583 /// <returns><c>true</c>, if the type file was found, <c>false</c> otherwise.</returns>
584 /// <param name="result">A typle that contains 1) the 'reltypefile', 2) the 'typefile', and 3) the file info</param>
585 bool TryFindTypeFile(string nsname, string typename, string basepath, out Tuple<string, string, FileInfo> result) {
586 string reltypefile = DocUtils.PathCombine (nsname, typename + ".xml");
587 string typefile = Path.Combine (basepath, reltypefile);
588 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
590 result = new Tuple<string, string, FileInfo> (reltypefile, typefile, file);
595 public string DoUpdateType (TypeDefinition type, string basepath, string dest)
597 if (type.Namespace == null)
598 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
600 if (!IsPublic (type))
603 // Must get the A+B form of the type name.
604 string typename = GetTypeFileName(type);
605 string nsname = DocUtils.GetNamespace (type);
607 // Find the file, if it exists
608 string[] searchLocations = new string[] {
612 if (MDocUpdater.HasDroppedNamespace (type)) {
613 // If dropping namespace, types may have moved into a couple of different places.
614 var newSearchLocations = searchLocations.Union (new string[] {
615 string.Format ("{0}.{1}", droppedNamespace, nsname),
616 nsname.Replace (droppedNamespace + ".", string.Empty),
617 MDocUpdater.droppedNamespace
620 searchLocations = newSearchLocations.ToArray ();
623 string reltypefile="", typefile="";
624 System.IO.FileInfo file = null;
626 foreach (var f in searchLocations) {
627 Tuple<string, string, FileInfo> result;
628 bool fileExists = TryFindTypeFile (f, typename, basepath, out result);
631 reltypefile = result.Item1;
632 typefile = result.Item2;
639 if (file == null || !file.Exists) {
640 // we were not able to find a file, let's use the original type informatio.
641 // so that we create the stub in the right place.
642 Tuple<string, string, FileInfo> result;
643 TryFindTypeFile (nsname, typename, basepath, out result);
645 reltypefile = result.Item1;
646 typefile = result.Item2;
650 string output = null;
653 } else if (dest == "-") {
656 output = Path.Combine (dest, reltypefile);
659 if (file != null && file.Exists) {
661 XmlDocument basefile = new XmlDocument();
663 basefile.Load(typefile);
664 } catch (Exception e) {
665 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
668 DoUpdateType2("Updating", basefile, type, output, false);
671 XmlElement td = StubType(type, output);
675 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
678 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
684 public void DoUpdateNS (string ns, string nspath, string outpath)
686 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
687 AssemblyDefinition assembly = assemblies [0];
689 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
690 XmlDocument basefile = new XmlDocument();
691 string typefile = Path.Combine(nspath, file.Name);
693 basefile.Load(typefile);
694 } catch (Exception e) {
695 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
699 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
700 TypeDefinition type = assembly.GetType(typename);
703 if (!string.IsNullOrWhiteSpace (droppedNamespace)) {
704 string nameWithNs = string.Format ("{0}.{1}", droppedNamespace, typename);
705 type = assembly.GetType (nameWithNs);
707 Warning ("Type no longer in assembly: " + typename);
714 seenTypes[type] = seenTypes;
715 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false);
718 // Stub types not in the directory
719 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
720 if (type.Namespace != ns || seenTypes.ContainsKey(type))
723 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"));
724 if (td == null) continue;
728 private static string GetTypeFileName (TypeReference type)
730 return filenameFormatter.GetName (type);
733 public static string GetTypeFileName (string typename)
735 StringBuilder filename = new StringBuilder (typename.Length);
739 for (int i = 0; i < typename.Length; ++i) {
740 char c = typename [i];
749 filename.Append ('`').Append ((numArgs+1).ToString());
764 return filename.ToString ();
767 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
769 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
770 index_assembly.SetAttribute ("Name", assembly.Name.Name);
771 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
773 AssemblyNameDefinition name = assembly.Name;
774 if (name.HasPublicKey) {
775 XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
776 var key = new StringBuilder (name.PublicKey.Length*3 + 2);
778 foreach (byte b in name.PublicKey)
779 key.AppendFormat ("{0,2:x2} ", b);
781 pubkey.InnerText = key.ToString ();
782 index_assembly.AppendChild (pubkey);
785 if (!string.IsNullOrEmpty (name.Culture)) {
786 XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
787 culture.InnerText = name.Culture;
788 index_assembly.AppendChild (culture);
791 MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
792 parent.AppendChild(index_assembly);
795 private void AddIndexType (TypeDefinition type, XmlElement index_types)
797 string typename = GetTypeFileName(type);
799 // Add namespace and type nodes into the index file as needed
800 string ns = DocUtils.GetNamespace (type);
801 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode ("Namespace[@Name='" + ns + "']");
802 if (nsnode == null) {
803 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
804 nsnode.SetAttribute ("Name", ns);
805 index_types.AppendChild (nsnode);
807 string doc_typename = GetDocTypeName (type);
808 XmlElement typenode = (XmlElement) nsnode.SelectSingleNode ("Type[@Name='" + typename + "']");
809 if (typenode == null) {
810 typenode = index_types.OwnerDocument.CreateElement ("Type");
811 typenode.SetAttribute ("Name", typename);
812 nsnode.AppendChild (typenode);
814 if (typename != doc_typename)
815 typenode.SetAttribute("DisplayName", doc_typename);
817 typenode.RemoveAttribute("DisplayName");
819 typenode.SetAttribute ("Kind", GetTypeKind (type));
822 private void DoUpdateAssemblies (string source, string dest)
824 string indexfile = dest + "/index.xml";
826 if (System.IO.File.Exists(indexfile)) {
827 index = new XmlDocument();
828 index.Load(indexfile);
831 ClearElement(index.DocumentElement, "Assembly");
832 ClearElement(index.DocumentElement, "Attributes");
834 index = CreateIndexStub();
837 string defaultTitle = "Untitled";
838 if (assemblies.Count == 1)
839 defaultTitle = assemblies[0].Name.Name;
840 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
842 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
843 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
844 index_assemblies.RemoveAll ();
847 HashSet<string> goodfiles = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
849 foreach (AssemblyDefinition assm in assemblies) {
850 AddIndexAssembly (assm, index_assemblies);
851 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
854 SortIndexEntries (index_types);
856 CleanupFiles (dest, goodfiles);
857 CleanupIndexTypes (index_types, goodfiles);
858 CleanupExtensions (index_types);
860 WriteFile (indexfile, FileMode.Create,
861 writer => WriteXml(index.DocumentElement, writer));
864 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
866 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
868 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
869 string typename = GetTypeFileName(type);
870 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0 || forwardedTypes.Contains (type.FullName))
873 string reltypepath = DoUpdateType (type, source, dest);
874 if (reltypepath == null)
877 // Add namespace and type nodes into the index file as needed
878 AddIndexType (type, index_types);
880 // Ensure the namespace index file exists
881 string namespaceToUse = type.Namespace;
882 if (HasDroppedNamespace(assembly)) {
883 namespaceToUse = string.Format ("{0}.{1}", droppedNamespace, namespaceToUse);
885 string onsdoc = DocUtils.PathCombine (dest, namespaceToUse + ".xml");
886 string nsdoc = DocUtils.PathCombine (dest, "ns-" + namespaceToUse + ".xml");
887 if (File.Exists (onsdoc)) {
888 File.Move (onsdoc, nsdoc);
891 if (!File.Exists (nsdoc)) {
892 Console.WriteLine("New Namespace File: " + type.Namespace);
893 WriteNamespaceStub(namespaceToUse, dest);
896 goodfiles.Add (reltypepath);
900 private static void SortIndexEntries (XmlElement indexTypes)
902 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
903 XmlNodeComparer c = new AttributeNameComparer ();
904 SortXmlNodes (indexTypes, namespaces, c);
906 for (int i = 0; i < namespaces.Count; ++i)
907 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
910 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
912 MyXmlNodeList l = new MyXmlNodeList (children.Count);
913 for (int i = 0; i < children.Count; ++i)
914 l.Add (children [i]);
916 for (int i = l.Count - 1; i > 0; --i) {
917 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
921 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
923 public abstract int Compare (XmlNode x, XmlNode y);
925 public int Compare (object x, object y)
927 return Compare ((XmlNode) x, (XmlNode) y);
931 class AttributeNameComparer : XmlNodeComparer {
934 public AttributeNameComparer ()
939 public AttributeNameComparer (string attribute)
941 this.attribute = attribute;
944 public override int Compare (XmlNode x, XmlNode y)
946 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
950 class VersionComparer : XmlNodeComparer {
951 public override int Compare (XmlNode x, XmlNode y)
953 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
954 string a = GetVersion (x.InnerText);
955 string b = GetVersion (y.InnerText);
956 return new Version (a).CompareTo (new Version (b));
959 static string GetVersion (string v)
961 int n = v.IndexOf ("x");
964 return v.Substring (0, n-1);
968 private static string GetTypeKind (TypeDefinition type)
971 return "Enumeration";
972 if (type.IsValueType)
974 if (type.IsInterface)
976 if (DocUtils.IsDelegate (type))
978 if (type.IsClass || type.FullName == "System.Enum") // FIXME
980 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
983 private static bool IsPublic (TypeDefinition type)
985 TypeDefinition decl = type;
986 while (decl != null) {
987 if (!(decl.IsPublic || decl.IsNestedPublic ||
988 decl.IsNestedFamily || decl.IsNestedFamily || decl.IsNestedFamilyOrAssembly)) {
991 decl = (TypeDefinition) decl.DeclaringType;
996 private void CleanupFiles (string dest, HashSet<string> goodfiles)
998 // Look for files that no longer correspond to types
999 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
1000 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
1001 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
1002 if (!goodfiles.Contains (relTypeFile)) {
1003 XmlDocument doc = new XmlDocument ();
1004 doc.Load (typefile.FullName);
1005 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
1006 string assemblyName = doc.SelectSingleNode ("/Type/AssemblyInfo/AssemblyName").InnerText;
1007 AssemblyDefinition assembly = assemblies.FirstOrDefault (a => a.Name.Name == assemblyName);
1008 if (e != null && !no_assembly_versions && assembly != null && assemblyName != null && UpdateAssemblyVersions(e, assembly, GetAssemblyVersions(assemblyName), false)) {
1009 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
1010 WriteXml(doc.DocumentElement, writer);
1011 goodfiles.Add (relTypeFile);
1015 if (string.IsNullOrWhiteSpace (PreserveTag)) { // only do this if there was no -preserve
1016 string newname = typefile.FullName + ".remove";
1017 try { System.IO.File.Delete(newname); } catch (Exception) { }
1018 try { typefile.MoveTo(newname); } catch (Exception) { }
1019 Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
1026 private static TextWriter OpenWrite (string path, FileMode mode)
1028 var w = new StreamWriter (
1029 new FileStream (path, mode),
1030 new UTF8Encoding (false)
1036 private string[] GetAssemblyVersions (string assemblyName)
1038 return (from a in assemblies
1039 where a.Name.Name == assemblyName
1040 select GetAssemblyVersion (a)).ToArray ();
1043 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
1045 // Look for type nodes that no longer correspond to types
1046 MyXmlNodeList remove = new MyXmlNodeList ();
1047 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
1048 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
1049 if (!goodfiles.Contains (fulltypename)) {
1050 remove.Add (typenode);
1053 foreach (XmlNode n in remove)
1054 n.ParentNode.RemoveChild (n);
1057 private void CleanupExtensions (XmlElement index_types)
1059 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
1060 if (extensionMethods.Count == 0) {
1063 index_types.SelectSingleNode ("/Overview").RemoveChild (e);
1067 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
1068 index_types.SelectSingleNode ("/Overview").AppendChild (e);
1072 extensionMethods.Sort (DefaultExtensionMethodComparer);
1073 foreach (XmlNode m in extensionMethods) {
1074 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
1078 class ExtensionMethodComparer : XmlNodeComparer {
1079 public override int Compare (XmlNode x, XmlNode y)
1081 XmlNode xLink = x.SelectSingleNode ("Member/Link");
1082 XmlNode yLink = y.SelectSingleNode ("Member/Link");
1084 int n = xLink.Attributes ["Type"].Value.CompareTo (
1085 yLink.Attributes ["Type"].Value);
1088 n = xLink.Attributes ["Member"].Value.CompareTo (
1089 yLink.Attributes ["Member"].Value);
1090 if (n == 0 && !object.ReferenceEquals (x, y))
1091 throw new InvalidOperationException ("Duplicate extension method found!");
1096 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
1098 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince)
1100 Console.WriteLine(message + ": " + type.FullName);
1102 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
1104 // Update type metadata
1105 UpdateType(basefile.DocumentElement, type);
1107 // Update existing members. Delete member nodes that no longer should be there,
1108 // and remember what members are already documented so we don't add them again.
1110 MyXmlNodeList todelete = new MyXmlNodeList ();
1112 foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
1113 XmlElement oldmember = info.Node;
1114 MemberReference oldmember2 = info.Member;
1115 string sig = oldmember2 != null ? memberFormatters [0].GetDeclaration (oldmember2) : null;
1117 // Interface implementations and overrides are deleted from the docs
1118 // unless the overrides option is given.
1119 if (oldmember2 != null && sig == null)
1122 // Deleted (or signature changed)
1123 if (oldmember2 == null) {
1124 if (!no_assembly_versions && UpdateAssemblyVersions (oldmember, type.Module.Assembly, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
1127 DeleteMember ("Member Removed", output, oldmember, todelete, type);
1132 if (seenmembers.ContainsKey (sig)) {
1133 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
1134 // ignore, already seen
1136 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
1137 DeleteMember ("Duplicate Member Found", output, oldmember, todelete, type);
1139 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
1143 // Update signature information
1146 // get all apistyles of sig from info.Node
1147 var styles = oldmember.GetElementsByTagName ("MemberSignature").Cast<XmlElement> ()
1148 .Where (x => x.GetAttribute ("Language") == "C#" && !seenmembers.ContainsKey(x.GetAttribute("Value")))
1149 .Select (x => x.GetAttribute ("Value"));
1151 foreach (var stylesig in styles) {
1152 seenmembers.Add (stylesig, oldmember);
1155 foreach (XmlElement oldmember in todelete)
1156 oldmember.ParentNode.RemoveChild (oldmember);
1159 if (!DocUtils.IsDelegate (type)) {
1160 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
1161 var typemembers = type.GetMembers()
1163 if (m is TypeDefinition) return false;
1164 string sig = memberFormatters [0].GetDeclaration (m);
1165 if (sig == null) return false;
1166 if (seenmembers.ContainsKey(sig)) return false;
1170 foreach (MemberReference m in typemembers) {
1171 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
1172 if (mm == null) continue;
1174 if (MDocUpdater.SwitchingToMagicTypes) {
1175 // this is a unified style API that obviously doesn't exist in the classic API. Let's mark
1176 // it with apistyle="unified", so that it's not displayed for classic style APIs
1177 mm.SetAttribute ("apistyle", "unified");
1180 members.AppendChild( mm );
1182 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
1187 // Import code snippets from files
1188 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
1189 if (!(code is XmlElement)) continue;
1190 string file = ((XmlElement)code).GetAttribute("src");
1191 string lang = ((XmlElement)code).GetAttribute("lang");
1193 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
1195 code.InnerText = src;
1199 if (insertSince && since != null) {
1200 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
1201 docs.AppendChild (CreateSinceNode (basefile));
1205 XmlElement d = basefile.DocumentElement ["Docs"];
1206 XmlElement m = basefile.DocumentElement ["Members"];
1207 if (d != null && m != null)
1208 basefile.DocumentElement.InsertBefore (
1209 basefile.DocumentElement.RemoveChild (d), m);
1210 SortTypeMembers (m);
1214 WriteXml(basefile.DocumentElement, Console.Out);
1216 FileInfo file = new FileInfo (output);
1217 if (!file.Directory.Exists) {
1218 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
1219 file.Directory.Create ();
1221 WriteFile (output, FileMode.Create,
1222 writer => WriteXml(basefile.DocumentElement, writer));
1226 private string GetCodeSource (string lang, string file)
1229 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
1230 // Grab the specified region
1231 string region = "#region " + file.Substring (anchorStart + 4);
1232 file = file.Substring (0, anchorStart + 3);
1234 using (StreamReader reader = new StreamReader (file)) {
1236 StringBuilder src = new StringBuilder ();
1238 while ((line = reader.ReadLine ()) != null) {
1239 if (line.Trim() == region) {
1240 indent = line.IndexOf (region);
1243 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
1248 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
1251 return src.ToString ();
1253 } catch (Exception e) {
1254 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
1255 file, region, show_exceptions ? e.ToString () : e.Message);
1260 using (StreamReader reader = new StreamReader (file))
1261 return reader.ReadToEnd ();
1262 } catch (Exception e) {
1263 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
1268 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete, TypeDefinition type)
1270 string format = output != null
1271 ? "{0}: File='{1}'; Signature='{4}'"
1272 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1276 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1277 member.Attributes ["MemberName"].Value,
1278 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
1279 if (!delete && MemberDocsHaveUserContent (member)) {
1280 Warning ("Member deletions must be enabled with the --delete option.");
1281 } else if (HasDroppedNamespace (type)) {
1282 // if we're dropping the namespace, add the "classic style"
1283 var existingAttribute = member.Attributes ["apistyle"];
1284 if (existingAttribute != null) {
1285 existingAttribute.Value = "classic";
1287 // add the attribute and do not remove
1288 XmlAttribute apistyleAttr = member.OwnerDocument.CreateAttribute ("apistyle");
1290 apistyleAttr.Value = "classic";
1292 member.Attributes.Append (apistyleAttr);
1294 } else if (!HasDroppedNamespace (type) && member.Attributes ["apistyle"] != null && member.Attributes ["apistyle"].Value == "unified") {
1295 // do nothing if there's an apistyle=new attribute and we haven't dropped the namespace
1296 } else if (!string.IsNullOrWhiteSpace (PreserveTag)) {
1299 todelete.Add (member);
1304 class MemberComparer : XmlNodeComparer {
1305 public override int Compare (XmlNode x, XmlNode y)
1308 string xMemberName = x.Attributes ["MemberName"].Value;
1309 string yMemberName = y.Attributes ["MemberName"].Value;
1311 // generic methods *end* with '>'
1312 // it's possible for explicitly implemented generic interfaces to
1313 // contain <...> without being a generic method
1314 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1315 (r = xMemberName.CompareTo (yMemberName)) != 0)
1319 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1320 xMemberName = xMemberName.Substring (0, lt);
1321 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1322 yMemberName = yMemberName.Substring (0, lt);
1323 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1326 // if @MemberName matches, then it's either two different types of
1327 // members sharing the same name, e.g. field & property, or it's an
1328 // overloaded method.
1329 // for different type, sort based on MemberType value.
1330 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1331 y.SelectSingleNode ("MemberType").InnerText);
1335 // same type -- must be an overloaded method. Sort based on type
1336 // parameter count, then parameter count, then by the parameter
1338 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1339 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1340 if (xTypeParams.Count != yTypeParams.Count)
1341 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1342 for (int i = 0; i < xTypeParams.Count; ++i) {
1343 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1344 yTypeParams [i].Attributes ["Name"].Value);
1349 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1350 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1351 if (xParams.Count != yParams.Count)
1352 return xParams.Count <= yParams.Count ? -1 : 1;
1353 for (int i = 0; i < xParams.Count; ++i) {
1354 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1355 yParams [i].Attributes ["Type"].Value);
1359 // all parameters match, but return value might not match if it was
1360 // changed between one version and another.
1361 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1362 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1363 if (xReturn != null && yReturn != null) {
1364 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1373 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1375 private static void SortTypeMembers (XmlNode members)
1377 if (members == null)
1379 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1382 private static bool MemberDocsHaveUserContent (XmlNode e)
1384 e = (XmlElement)e.SelectSingleNode("Docs");
1385 if (e == null) return false;
1386 foreach (XmlElement d in e.SelectNodes("*"))
1387 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1392 // UPDATE HELPER FUNCTIONS
1394 // CREATE A STUB DOCUMENTATION FILE
1396 public XmlElement StubType (TypeDefinition type, string output)
1398 string typesig = typeFormatters [0].GetDeclaration (type);
1399 if (typesig == null) return null; // not publicly visible
1401 XmlDocument doc = new XmlDocument();
1402 XmlElement root = doc.CreateElement("Type");
1403 doc.AppendChild (root);
1405 DoUpdateType2 ("New Type", doc, type, output, true);
1410 private XmlElement CreateSinceNode (XmlDocument doc)
1412 XmlElement s = doc.CreateElement ("since");
1413 s.SetAttribute ("version", since);
1417 // STUBBING/UPDATING FUNCTIONS
1419 public void UpdateType (XmlElement root, TypeDefinition type)
1421 root.SetAttribute("Name", GetDocTypeName (type));
1422 root.SetAttribute("FullName", GetDocTypeFullName (type));
1424 foreach (MemberFormatter f in typeFormatters) {
1425 string element = "TypeSignature[@Language='" + f.Language + "']";
1426 string valueToUse = f.GetDeclaration (type);
1429 root.SelectNodes (element).Cast<XmlElement> ().ToArray (),
1430 x => x.GetAttribute ("Value") == valueToUse,
1431 x => x.SetAttribute ("Value", valueToUse),
1433 var node = WriteElementAttribute (root, element, "Language", f.Language, forceNewElement: true);
1434 var newnode = WriteElementAttribute (root, node, "Value", valueToUse);
1440 string assemblyInfoNodeFilter = MDocUpdater.HasDroppedNamespace (type) ? "[@apistyle='unified']" : "[not(@apistyle) or @apistyle='classic']";
1443 root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ().ToArray (),
1444 x => x.SelectSingleNode("AssemblyName").InnerText == type.Module.Assembly.Name.Name,
1445 x => WriteElementText(x, "AssemblyName", type.Module.Assembly.Name.Name),
1447 XmlElement ass = WriteElement(root, "AssemblyInfo", forceNewElement:true);
1449 if (MDocUpdater.HasDroppedNamespace (type)) ass.SetAttribute ("apistyle", "unified");
1457 foreach(var ass in root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ())
1459 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1460 if (!no_assembly_versions) {
1461 UpdateAssemblyVersions (root, type, true);
1464 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1465 foreach (var version in versions)
1466 ass.RemoveChild (version);
1468 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1469 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1471 ClearElement(ass, "AssemblyCulture");
1474 // Why-oh-why do we put assembly attributes in each type file?
1475 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1476 // since they're outdated in current docs, and a waste of space.
1477 //MakeAttributes(ass, type.Assembly, true);
1478 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1479 if (assattrs != null)
1480 ass.RemoveChild(assattrs);
1482 NormalizeWhitespace(ass);
1485 if (type.IsGenericType ()) {
1486 MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
1488 ClearElement(root, "TypeParameters");
1491 if (type.BaseType != null) {
1492 XmlElement basenode = WriteElement(root, "Base");
1494 string basetypename = GetDocTypeFullName (type.BaseType);
1495 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1496 WriteElementText(root, "Base/BaseTypeName", basetypename);
1498 // Document how this type instantiates the generic parameters of its base type
1499 TypeReference origBase = type.BaseType.GetElementType ();
1500 if (origBase.IsGenericType ()) {
1501 ClearElement(basenode, "BaseTypeArguments");
1502 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1503 IList<TypeReference> baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1504 IList<GenericParameter> baseGenParams = origBase.GenericParameters;
1505 if (baseGenArgs.Count != baseGenParams.Count)
1506 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1507 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1508 GenericParameter param = baseGenParams [i];
1509 TypeReference value = baseGenArgs [i];
1511 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1512 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1513 bta.AppendChild(arg);
1514 arg.SetAttribute ("TypeParamName", param.Name);
1515 arg.InnerText = GetDocTypeFullName (value);
1519 ClearElement(root, "Base");
1522 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1523 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1524 List<string> interface_names = userInterfaces
1525 .Select (iface => GetDocTypeFullName (iface))
1529 XmlElement interfaces = WriteElement(root, "Interfaces");
1530 interfaces.RemoveAll();
1531 foreach (string iname in interface_names) {
1532 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1533 interfaces.AppendChild(iface);
1534 WriteElementText(iface, "InterfaceName", iname);
1537 ClearElement(root, "Interfaces");
1540 MakeAttributes (root, GetCustomAttributes (type), type);
1542 if (DocUtils.IsDelegate (type)) {
1543 MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
1544 var member = type.GetMethod ("Invoke");
1545 MakeParameters(root, member, member.Parameters);
1546 MakeReturnValue(root, member);
1549 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1550 MakeDocNode (typeInfo);
1552 if (!DocUtils.IsDelegate (type))
1553 WriteElement (root, "Members");
1555 OrderTypeNodes (root, root.ChildNodes);
1556 NormalizeWhitespace(root);
1559 static readonly string[] TypeNodeOrder = {
1563 "ThreadingSafetyStatement",
1564 "ThreadSafetyStatement",
1576 static void OrderTypeNodes (XmlNode member, XmlNodeList children)
1578 ReorderNodes (member, children, TypeNodeOrder);
1581 internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1583 List<T> l = new List<T> (list);
1588 private void UpdateMember (DocsNodeInfo info)
1590 XmlElement me = (XmlElement) info.Node;
1591 MemberReference mi = info.Member;
1593 foreach (MemberFormatter f in memberFormatters) {
1594 string element = "MemberSignature[@Language='" + f.Language + "']";
1596 var valueToUse = f.GetDeclaration (mi);
1599 me.SelectNodes (element).Cast<XmlElement> ().ToArray(),
1600 x => x.GetAttribute("Value") == valueToUse,
1601 x => x.SetAttribute ("Value", valueToUse),
1603 var node = WriteElementAttribute (me, element, "Language", f.Language, forceNewElement:true);
1604 var newNode = WriteElementAttribute (me, node, "Value", valueToUse);
1611 WriteElementText(me, "MemberType", GetMemberType(mi));
1613 if (!no_assembly_versions) {
1614 UpdateAssemblyVersions (me, mi, true);
1617 ClearElement (me, "AssemblyInfo");
1620 MakeAttributes (me, GetCustomAttributes (mi), mi.DeclaringType);
1622 MakeReturnValue(me, mi, MDocUpdater.HasDroppedNamespace(mi));
1623 if (mi is MethodReference) {
1624 MethodReference mb = (MethodReference) mi;
1625 if (mb.IsGenericMethod ())
1626 MakeTypeParameters (me, mb.GenericParameters, mi, MDocUpdater.HasDroppedNamespace(mi));
1628 MakeParameters(me, mi, MDocUpdater.HasDroppedNamespace(mi));
1631 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1632 WriteElementText(me, "MemberValue", fieldValue);
1634 info.Node = WriteElement (me, "Docs");
1636 OrderMemberNodes (me, me.ChildNodes);
1637 UpdateExtensionMethods (me, info);
1640 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, MemberReference member) {
1641 AddXmlNode (relevant, valueMatches, setValue, makeNewNode, member.Module);
1644 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, TypeDefinition type) {
1645 AddXmlNode (relevant, valueMatches, setValue, makeNewNode, type.Module);
1648 /// <summary>Adds an xml node, reusing the node if it's available</summary>
1649 /// <param name="relevant">The existing set of nodes</param>
1650 /// <param name="valueMatches">Checks to see if the node's value matches what you're trying to write.</param>
1651 /// <param name="setValue">Sets the node's value</param>
1652 /// <param name="makeNewNode">Creates a new node, if valueMatches returns false.</param>
1653 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, ModuleDefinition module)
1655 bool shouldDuplicate = MDocUpdater.HasDroppedNamespace (module);
1656 var styleToUse = shouldDuplicate ? ApiStyle.Unified : ApiStyle.Classic;
1657 var existing = relevant;
1659 bool addedOldApiStyle = false;
1661 if (shouldDuplicate) {
1662 existing = existing.Where (n => n.HasApiStyle (styleToUse)).ToArray ();
1663 foreach (var n in relevant.Where (n => n.DoesNotHaveApiStyle (styleToUse))) {
1664 if (valueMatches (n)) {
1668 n.AddApiStyle (ApiStyle.Classic);
1669 addedOldApiStyle = true;
1674 if (!existing.Any ()) {
1675 var newNode = makeNewNode ();
1676 if (shouldDuplicate && addedOldApiStyle) {
1677 newNode.AddApiStyle (ApiStyle.Unified);
1681 var itemToReuse = existing.First ();
1682 setValue (itemToReuse);
1684 if (shouldDuplicate && addedOldApiStyle) {
1685 itemToReuse.AddApiStyle (styleToUse);
1691 static readonly string[] MemberNodeOrder = {
1706 static void OrderMemberNodes (XmlNode member, XmlNodeList children)
1708 ReorderNodes (member, children, MemberNodeOrder);
1711 static void ReorderNodes (XmlNode node, XmlNodeList children, string[] ordering)
1713 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1714 for (int i = 0; i < ordering.Length; ++i) {
1715 for (int j = 0; j < children.Count; ++j) {
1716 XmlNode c = children [j];
1717 if (c.Name == ordering [i]) {
1718 newChildren.Add (c);
1722 if (newChildren.Count >= 0)
1723 node.PrependChild ((XmlNode) newChildren [0]);
1724 for (int i = 1; i < newChildren.Count; ++i) {
1725 XmlNode prev = (XmlNode) newChildren [i-1];
1726 XmlNode cur = (XmlNode) newChildren [i];
1727 node.RemoveChild (cur);
1728 node.InsertAfter (cur, prev);
1732 IEnumerable<string> GetCustomAttributes (MemberReference mi)
1734 IEnumerable<string> attrs = Enumerable.Empty<string>();
1736 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1738 attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1740 PropertyDefinition pd = mi as PropertyDefinition;
1742 if (pd.GetMethod != null)
1743 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1744 if (pd.SetMethod != null)
1745 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1748 EventDefinition ed = mi as EventDefinition;
1750 if (ed.AddMethod != null)
1751 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1752 if (ed.RemoveMethod != null)
1753 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1759 IEnumerable<string> GetCustomAttributes (IList<CustomAttribute> attributes, string prefix)
1761 foreach (CustomAttribute attribute in attributes.OrderBy (ca => ca.AttributeType.FullName)) {
1763 TypeDefinition attrType = attribute.AttributeType as TypeDefinition;
1764 if (attrType != null && !IsPublic (attrType))
1766 if (slashdocFormatter.GetName (attribute.AttributeType) == null)
1769 if (Array.IndexOf (IgnorableAttributes, attribute.AttributeType.FullName) >= 0)
1772 StringList fields = new StringList ();
1774 for (int i = 0; i < attribute.ConstructorArguments.Count; ++i) {
1775 CustomAttributeArgument argument = attribute.ConstructorArguments [i];
1776 fields.Add (MakeAttributesValueString (
1781 (from namedArg in attribute.Fields
1782 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value })
1784 (from namedArg in attribute.Properties
1785 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value }))
1786 .OrderBy (v => v.Name);
1787 foreach (var d in namedArgs)
1788 fields.Add (string.Format ("{0}={1}", d.Name,
1789 MakeAttributesValueString (d.Value, d.Type)));
1791 string a2 = String.Join(", ", fields.ToArray ());
1792 if (a2 != "") a2 = "(" + a2 + ")";
1794 string name = attribute.GetDeclaringType();
1795 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1796 yield return prefix + name + a2;
1800 static readonly string[] ValidExtensionMembers = {
1809 static readonly string[] ValidExtensionDocMembers = {
1815 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1817 MethodDefinition me = info.Member as MethodDefinition;
1820 if (info.Parameters.Count < 1)
1822 if (!DocUtils.IsExtensionMethod (me))
1825 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1826 XmlNode member = e.CloneNode (true);
1827 em.AppendChild (member);
1828 RemoveExcept (member, ValidExtensionMembers);
1829 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1830 WriteElementText (member, "MemberType", "ExtensionMethod");
1831 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1832 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1833 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1834 member.AppendChild (link);
1835 AddTargets (em, info);
1837 extensionMethods.Add (em);
1840 private static void RemoveExcept (XmlNode node, string[] except)
1844 MyXmlNodeList remove = null;
1845 foreach (XmlNode n in node.ChildNodes) {
1846 if (Array.BinarySearch (except, n.Name) < 0) {
1848 remove = new MyXmlNodeList ();
1853 foreach (XmlNode n in remove)
1854 node.RemoveChild (n);
1857 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1859 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1860 member.PrependChild (targets);
1861 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1862 AppendElementAttributeText (targets, "Target", "Type",
1863 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1866 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1867 IList<TypeReference> constraints = gp.Constraints;
1868 if (constraints.Count == 0)
1869 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1871 foreach (TypeReference c in constraints)
1872 AppendElementAttributeText(targets, "Target", "Type",
1873 slashdocFormatter.GetDeclaration (c));
1877 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1880 TypeDefinition type = field.DeclaringType.Resolve ();
1881 if (type != null && type.IsEnum) return false;
1883 if (type != null && type.IsGenericType ()) return false;
1884 if (!field.HasConstant)
1886 if (field.IsLiteral) {
1887 object val = field.Constant;
1888 if (val == null) value = "null";
1889 else if (val is Enum) value = val.ToString();
1890 else if (val is IFormattable) {
1891 value = ((IFormattable)val).ToString();
1893 value = "\"" + value + "\"";
1895 if (value != null && value != "")
1901 // XML HELPER FUNCTIONS
1903 internal static XmlElement WriteElement(XmlNode parent, string element, bool forceNewElement = false) {
1904 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1905 if (ret == null || forceNewElement) {
1906 string[] path = element.Split('/');
1907 foreach (string p in path) {
1908 ret = (XmlElement)parent.SelectSingleNode(p);
1909 if (ret == null || forceNewElement) {
1911 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1912 ename = ename.Substring(0, ename.IndexOf('['));
1913 ret = parent.OwnerDocument.CreateElement(ename);
1914 parent.AppendChild(ret);
1923 private static XmlElement WriteElementText(XmlNode parent, string element, string value, bool forceNewElement = false) {
1924 XmlElement node = WriteElement(parent, element, forceNewElement: forceNewElement);
1925 node.InnerText = value;
1929 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1931 XmlElement n = parent.OwnerDocument.CreateElement (element);
1932 parent.AppendChild (n);
1933 n.InnerText = value;
1937 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1939 XmlElement n = parent.OwnerDocument.CreateElement (element);
1940 parent.AppendChild (n);
1941 n.SetAttribute (attribute, value);
1945 internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
1947 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1948 dest.AppendChild (copy);
1952 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1953 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1956 node = WriteElement(parent, element);
1957 node.InnerText = value;
1959 private static XmlElement WriteElementAttribute(XmlElement parent, string element, string attribute, string value, bool forceNewElement = false) {
1960 XmlElement node = WriteElement(parent, element, forceNewElement:forceNewElement);
1961 return WriteElementAttribute (parent, node, attribute, value);
1963 private static XmlElement WriteElementAttribute(XmlElement parent, XmlElement node, string attribute, string value) {
1964 if (node.GetAttribute (attribute) != value) {
1965 node.SetAttribute (attribute, value);
1969 internal static void ClearElement(XmlElement parent, string name) {
1970 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1972 parent.RemoveChild(node);
1975 // DOCUMENTATION HELPER FUNCTIONS
1977 private void MakeDocNode (DocsNodeInfo info)
1979 List<GenericParameter> genericParams = info.GenericParameters;
1980 IList<ParameterDefinition> parameters = info.Parameters;
1981 TypeReference returntype = info.ReturnType;
1982 bool returnisreturn = info.ReturnIsReturn;
1983 XmlElement e = info.Node;
1984 bool addremarks = info.AddRemarks;
1986 WriteElementInitialText(e, "summary", "To be added.");
1988 if (parameters != null) {
1989 string[] values = new string [parameters.Count];
1990 for (int i = 0; i < values.Length; ++i)
1991 values [i] = parameters [i].Name;
1992 UpdateParameters (e, "param", values);
1995 if (genericParams != null) {
1996 string[] values = new string [genericParams.Count];
1997 for (int i = 0; i < values.Length; ++i)
1998 values [i] = genericParams [i].Name;
1999 UpdateParameters (e, "typeparam", values);
2002 string retnodename = null;
2003 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
2004 retnodename = returnisreturn ? "returns" : "value";
2005 string retnodename_other = !returnisreturn ? "returns" : "value";
2007 // If it has a returns node instead of a value node, change its name.
2008 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
2009 if (retother != null) {
2010 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
2011 foreach (XmlNode node in retother)
2012 retnode.AppendChild(node.CloneNode(true));
2013 e.ReplaceChild(retnode, retother);
2015 WriteElementInitialText(e, retnodename, "To be added.");
2018 ClearElement(e, "returns");
2019 ClearElement(e, "value");
2023 WriteElementInitialText(e, "remarks", "To be added.");
2025 if (exceptions.HasValue && info.Member != null &&
2026 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
2027 UpdateExceptions (e, info.Member);
2030 foreach (DocumentationImporter importer in importers)
2031 importer.ImportDocumentation (info);
2033 OrderDocsNodes (e, e.ChildNodes);
2034 NormalizeWhitespace(e);
2037 static readonly string[] DocsNodeOrder = {
2038 "typeparam", "param", "summary", "returns", "value", "remarks",
2041 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
2043 ReorderNodes (docs, children, DocsNodeOrder);
2047 private void UpdateParameters (XmlElement e, string element, string[] values)
2049 if (values != null) {
2050 XmlNode[] paramnodes = new XmlNode[values.Length];
2052 // Some documentation had param nodes with leading spaces.
2053 foreach (XmlElement paramnode in e.SelectNodes(element)){
2054 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
2057 // If a member has only one parameter, we can track changes to
2058 // the name of the parameter easily.
2059 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
2060 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
2063 bool reinsert = false;
2065 // Pick out existing and still-valid param nodes, and
2066 // create nodes for parameters not in the file.
2067 Hashtable seenParams = new Hashtable();
2068 for (int pi = 0; pi < values.Length; pi++) {
2069 string p = values [pi];
2072 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
2073 if (paramnodes[pi] != null) continue;
2075 XmlElement pe = e.OwnerDocument.CreateElement(element);
2076 pe.SetAttribute("name", p);
2077 pe.InnerText = "To be added.";
2078 paramnodes[pi] = pe;
2082 // Remove parameters that no longer exist and check all params are in the right order.
2084 MyXmlNodeList todelete = new MyXmlNodeList ();
2085 foreach (XmlElement paramnode in e.SelectNodes(element)) {
2086 string name = paramnode.GetAttribute("name");
2087 if (!seenParams.ContainsKey(name)) {
2088 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2089 Warning ("The following param node can only be deleted if the --delete option is given: ");
2090 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
2092 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
2093 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2097 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
2098 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2099 e.ParentNode.Attributes ["MemberName"].Value,
2102 Warning ("\tValue={0}", paramnode.OuterXml);
2104 todelete.Add (paramnode);
2109 if ((int)seenParams[name] != idx)
2115 foreach (XmlNode n in todelete) {
2116 n.ParentNode.RemoveChild (n);
2119 // Re-insert the parameter nodes at the top of the doc section.
2121 for (int pi = values.Length-1; pi >= 0; pi--)
2122 e.PrependChild(paramnodes[pi]);
2124 // Clear all existing param nodes
2125 foreach (XmlNode paramnode in e.SelectNodes(element)) {
2126 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2127 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
2128 Console.WriteLine(paramnode.OuterXml);
2130 paramnode.ParentNode.RemoveChild(paramnode);
2136 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
2138 string existingName = pe.GetAttribute ("name");
2139 pe.SetAttribute ("name", newName);
2140 if (existingName == newName)
2142 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
2143 if (paramref.GetAttribute ("name").Trim () == existingName)
2144 paramref.SetAttribute ("name", newName);
2147 class CrefComparer : XmlNodeComparer {
2149 public CrefComparer ()
2153 public override int Compare (XmlNode x, XmlNode y)
2155 string xType = x.Attributes ["cref"].Value;
2156 string yType = y.Attributes ["cref"].Value;
2157 string xNamespace = GetNamespace (xType);
2158 string yNamespace = GetNamespace (yType);
2160 int c = xNamespace.CompareTo (yNamespace);
2163 return xType.CompareTo (yType);
2166 static string GetNamespace (string type)
2168 int n = type.LastIndexOf ('.');
2170 return type.Substring (0, n);
2171 return string.Empty;
2175 private void UpdateExceptions (XmlNode docs, MemberReference member)
2177 string indent = new string (' ', 10);
2178 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
2179 string cref = slashdocFormatter.GetDeclaration (source.Exception);
2180 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
2183 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
2184 e.SetAttribute ("cref", cref);
2185 e.InnerXml = "To be added; from:\n" + indent + "<see cref=\"" +
2186 string.Join ("\" />,\n" + indent + "<see cref=\"",
2187 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
2188 .OrderBy (s => s)) +
2190 docs.AppendChild (e);
2192 SortXmlNodes (docs, docs.SelectNodes ("exception"),
2193 new CrefComparer ());
2196 private static void NormalizeWhitespace(XmlElement e) {
2197 // Remove all text and whitespace nodes from the element so it
2198 // is outputted with nice indentation and no blank lines.
2199 ArrayList deleteNodes = new ArrayList();
2200 foreach (XmlNode n in e)
2201 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2203 foreach (XmlNode n in deleteNodes)
2204 n.ParentNode.RemoveChild(n);
2207 private static bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
2209 TypeDefinition type = member as TypeDefinition;
2211 type = member.DeclaringType as TypeDefinition;
2212 return UpdateAssemblyVersions(root, type.Module.Assembly, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
2215 private static string GetAssemblyVersion (AssemblyDefinition assembly)
2217 return assembly.Name.Version.ToString();
2220 private static bool UpdateAssemblyVersions(XmlElement root, AssemblyDefinition assembly, string[] assemblyVersions, bool add)
2222 XmlElement av = (XmlElement) root.SelectSingleNode ("AssemblyVersions");
2224 // AssemblyVersions is not part of the spec
2225 root.RemoveChild (av);
2228 string oldNodeFilter = "AssemblyInfo[not(@apistyle) or @apistyle='classic']";
2229 string newNodeFilter = "AssemblyInfo[@apistyle='unified']";
2230 string thisNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? newNodeFilter : oldNodeFilter;
2231 string thatNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? oldNodeFilter : newNodeFilter;
2233 XmlElement e = (XmlElement) root.SelectSingleNode (thisNodeFilter);
2235 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2237 if (MDocUpdater.HasDroppedNamespace (assembly)) {
2238 e.SetAttribute ("apistyle", "unified");
2241 root.AppendChild(e);
2244 var thatNode = (XmlElement) root.SelectSingleNode (thatNodeFilter);
2245 if (MDocUpdater.HasDroppedNamespace (assembly) && thatNode != null) {
2246 // there's a classic node, we should add apistyles
2247 e.SetAttribute ("apistyle", "unified");
2248 thatNode.SetAttribute ("apistyle", "classic");
2251 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
2252 .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
2254 // matches.Count > 0 && add: ignore -- already present
2255 if (matches.Count > 0 && !add) {
2256 foreach (XmlNode c in matches)
2259 else if (matches.Count == 0 && add) {
2260 foreach (string sv in assemblyVersions) {
2261 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2267 // matches.Count == 0 && !add: ignore -- already not present
2268 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2269 SortXmlNodes (e, avs, new VersionComparer ());
2271 bool anyNodesLeft = avs.Count != 0;
2272 if (!anyNodesLeft) {
2273 e.ParentNode.RemoveChild (e);
2275 return anyNodesLeft;
2278 // FIXME: get TypeReferences instead of string comparison?
2279 private static string[] IgnorableAttributes = {
2280 // Security related attributes
2281 "System.Reflection.AssemblyKeyFileAttribute",
2282 "System.Reflection.AssemblyDelaySignAttribute",
2283 // Present in @RefType
2284 "System.Runtime.InteropServices.OutAttribute",
2285 // For naming the indexer to use when not using indexers
2286 "System.Reflection.DefaultMemberAttribute",
2287 // for decimal constants
2288 "System.Runtime.CompilerServices.DecimalConstantAttribute",
2289 // compiler generated code
2290 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
2291 // more compiler generated code, e.g. iterator methods
2292 "System.Diagnostics.DebuggerHiddenAttribute",
2293 "System.Runtime.CompilerServices.FixedBufferAttribute",
2294 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
2295 // extension methods
2296 "System.Runtime.CompilerServices.ExtensionAttribute",
2297 // Used to differentiate 'object' from C#4 'dynamic'
2298 "System.Runtime.CompilerServices.DynamicAttribute",
2301 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes, TypeReference t=null)
2303 if (!attributes.Any ()) {
2304 ClearElement (root, "Attributes");
2308 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2312 e = root.OwnerDocument.CreateElement("Attributes");
2314 foreach (string attribute in attributes) {
2315 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2318 WriteElementText(ae, "AttributeName", attribute);
2321 if (e.ParentNode == null)
2322 root.AppendChild(e);
2324 NormalizeWhitespace(e);
2327 public static string MakeAttributesValueString (object v, TypeReference valueType)
2331 if (valueType.FullName == "System.Type") {
2332 var vTypeRef = v as TypeReference;
2333 if (vTypeRef != null)
2334 return "typeof(" + NativeTypeManager.GetTranslatedName (vTypeRef) + ")"; // TODO: drop NS handling
2336 return "typeof(" + v.ToString () + ")";
2338 if (valueType.FullName == "System.String")
2339 return "\"" + v.ToString () + "\"";
2340 if (valueType.FullName == "System.Char")
2341 return "'" + v.ToString () + "'";
2343 return (bool)v ? "true" : "false";
2344 TypeDefinition valueDef = valueType.Resolve ();
2345 if (valueDef == null || !valueDef.IsEnum)
2346 return v.ToString ();
2347 string typename = GetDocTypeFullName (valueType);
2348 var values = GetEnumerationValues (valueDef);
2349 long c = ToInt64 (v);
2350 if (values.ContainsKey (c))
2351 return typename + "." + values [c];
2352 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
2353 return string.Join (" | ",
2354 (from i in values.Keys
2356 select typename + "." + values [i])
2357 .DefaultIfEmpty (v.ToString ()).ToArray ());
2359 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
2362 private static Dictionary<long, string> GetEnumerationValues (TypeDefinition type)
2364 var values = new Dictionary<long, string> ();
2366 (from f in type.Fields
2367 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
2369 values [ToInt64 (f.Constant)] = f.Name;
2374 static long ToInt64 (object value)
2377 return (long) (ulong) value;
2378 return Convert.ToInt64 (value);
2381 private void MakeParameters (XmlElement root, MemberReference member, IList<ParameterDefinition> parameters, bool shouldDuplicateWithNew=false)
2383 XmlElement e = WriteElement(root, "Parameters");
2386 foreach (ParameterDefinition p in parameters) {
2390 var ptype = GetDocParameterType (p.ParameterType);
2391 var newPType = ptype;
2393 if (MDocUpdater.SwitchingToMagicTypes) {
2394 newPType = NativeTypeManager.ConvertFromNativeType (ptype);
2397 // now find the existing node, if it's there so we can reuse it.
2398 var nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2399 .Cast<XmlElement> ().Where (x => x.GetAttribute ("Name") == p.Name)
2402 if (nodes.Count () == 0) {
2403 // wasn't found, let's make sure it wasn't just cause the param name was changed
2404 nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2405 .Cast<XmlElement> ()
2406 .Skip (i) // this makes sure we don't inadvertently "reuse" nodes when adding new ones
2407 .Where (x => x.GetAttribute ("Name") != p.Name && (x.GetAttribute ("Type") == ptype || x.GetAttribute ("Type") == newPType))
2408 .Take(1) // there might be more than one that meets this parameter ... only take the first.
2413 x => x.GetAttribute ("Type") == ptype,
2414 x => x.SetAttribute ("Type", ptype),
2416 pe = root.OwnerDocument.CreateElement ("Parameter");
2419 pe.SetAttribute ("Name", p.Name);
2420 pe.SetAttribute ("Type", ptype);
2421 if (p.ParameterType is ByReferenceType) {
2423 pe.SetAttribute ("RefType", "out");
2425 pe.SetAttribute ("RefType", "ref");
2428 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
2437 private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams, MemberReference member, bool shouldDuplicateWithNew)
2439 if (typeParams == null || typeParams.Count == 0) {
2440 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2442 root.RemoveChild (f);
2445 XmlElement e = WriteElement(root, "TypeParameters");
2447 var nodes = e.SelectNodes ("TypeParameter").Cast<XmlElement> ().ToArray ();
2449 foreach (GenericParameter t in typeParams) {
2451 IList<TypeReference> constraints = t.Constraints;
2452 GenericParameterAttributes attrs = t.Attributes;
2458 var baseType = e.SelectSingleNode("BaseTypeName");
2459 // TODO: should this comparison take into account BaseTypeName?
2460 return x.GetAttribute("Name") == t.Name;
2462 x => {}, // no additional action required
2465 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2467 pe.SetAttribute("Name", t.Name);
2468 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""), t.DeclaringType);
2469 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2470 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2478 ce = root.OwnerDocument.CreateElement ("Constraints");
2480 pe.AppendChild (ce);
2481 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2482 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2483 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2484 AppendElementText (ce, "ParameterAttribute", "Covariant");
2485 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2486 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2487 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2488 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2489 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2490 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2491 foreach (TypeReference c in constraints) {
2492 TypeDefinition cd = c.Resolve ();
2493 AppendElementText (ce,
2494 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2495 GetDocTypeFullName (c));
2504 private void MakeParameters (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew)
2506 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2507 MakeParameters (root, mi, ((MethodDefinition)mi).Parameters, shouldDuplicateWithNew);
2508 else if (mi is MethodDefinition) {
2509 MethodDefinition mb = (MethodDefinition) mi;
2510 IList<ParameterDefinition> parameters = mb.Parameters;
2511 MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
2512 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2513 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2514 p.SetAttribute ("RefType", "this");
2517 else if (mi is PropertyDefinition) {
2518 IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
2519 if (parameters.Count > 0)
2520 MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
2524 else if (mi is FieldDefinition) return;
2525 else if (mi is EventDefinition) return;
2526 else throw new ArgumentException();
2529 internal static string GetDocParameterType (TypeReference type)
2531 return GetDocTypeFullName (type).Replace ("@", "&");
2534 private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes, bool shouldDuplicateWithNew=false)
2536 XmlElement e = WriteElement(root, "ReturnValue");
2537 var valueToUse = GetDocTypeFullName (type);
2539 AddXmlNode (e.SelectNodes("ReturnType").Cast<XmlElement> ().ToArray (),
2540 x => x.InnerText == valueToUse,
2541 x => x.InnerText = valueToUse,
2543 var newNode = WriteElementText(e, "ReturnType", valueToUse, forceNewElement: true);
2544 if (attributes != null)
2545 MakeAttributes(e, GetCustomAttributes (attributes, ""), type);
2552 private void MakeReturnValue (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew=false)
2554 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2556 else if (mi is MethodDefinition)
2557 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes, shouldDuplicateWithNew);
2558 else if (mi is PropertyDefinition)
2559 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null, shouldDuplicateWithNew);
2560 else if (mi is FieldDefinition)
2561 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null, shouldDuplicateWithNew);
2562 else if (mi is EventDefinition)
2563 MakeReturnValue (root, ((EventDefinition)mi).EventType, null, shouldDuplicateWithNew);
2565 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2568 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2570 MemberReference mi = info.Member;
2571 if (mi is TypeDefinition) return null;
2573 string sigs = memberFormatters [0].GetDeclaration (mi);
2574 if (sigs == null) return null; // not publicly visible
2576 // no documentation for property/event accessors. Is there a better way of doing this?
2577 if (mi.Name.StartsWith("get_")) return null;
2578 if (mi.Name.StartsWith("set_")) return null;
2579 if (mi.Name.StartsWith("add_")) return null;
2580 if (mi.Name.StartsWith("remove_")) return null;
2581 if (mi.Name.StartsWith("raise_")) return null;
2583 XmlElement me = doc.CreateElement("Member");
2584 me.SetAttribute("MemberName", GetMemberName (mi));
2588 if (exceptions.HasValue &&
2589 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2590 UpdateExceptions (info.Node, info.Member);
2592 if (since != null) {
2593 XmlNode docs = me.SelectSingleNode("Docs");
2594 docs.AppendChild (CreateSinceNode (doc));
2600 internal static string GetMemberName (MemberReference mi)
2602 MethodDefinition mb = mi as MethodDefinition;
2604 PropertyDefinition pi = mi as PropertyDefinition;
2607 return DocUtils.GetPropertyName (pi);
2609 StringBuilder sb = new StringBuilder (mi.Name.Length);
2610 if (!DocUtils.IsExplicitlyImplemented (mb))
2611 sb.Append (mi.Name);
2613 TypeReference iface;
2614 MethodReference ifaceMethod;
2615 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2616 sb.Append (GetDocTypeFullName (iface));
2618 sb.Append (ifaceMethod.Name);
2620 if (mb.IsGenericMethod ()) {
2621 IList<GenericParameter> typeParams = mb.GenericParameters;
2622 if (typeParams.Count > 0) {
2624 sb.Append (typeParams [0].Name);
2625 for (int i = 1; i < typeParams.Count; ++i)
2626 sb.Append (",").Append (typeParams [i].Name);
2630 return sb.ToString ();
2633 /// SIGNATURE GENERATION FUNCTIONS
2634 internal static bool IsPrivate (MemberReference mi)
2636 return memberFormatters [0].GetDeclaration (mi) == null;
2639 internal static string GetMemberType (MemberReference mi)
2641 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2642 return "Constructor";
2643 if (mi is MethodDefinition)
2645 if (mi is PropertyDefinition)
2647 if (mi is FieldDefinition)
2649 if (mi is EventDefinition)
2651 throw new ArgumentException();
2654 private static string GetDocTypeName (TypeReference type)
2656 return docTypeFormatter.GetName (type);
2659 internal static string GetDocTypeFullName (TypeReference type)
2661 return DocTypeFullMemberFormatter.Default.GetName (type);
2664 internal static string GetXPathForMember (DocumentationMember member)
2666 StringBuilder xpath = new StringBuilder ();
2667 xpath.Append ("//Members/Member[@MemberName=\"")
2668 .Append (member.MemberName)
2670 if (member.Parameters != null && member.Parameters.Count > 0) {
2671 xpath.Append ("/Parameters[count(Parameter) = ")
2672 .Append (member.Parameters.Count);
2673 for (int i = 0; i < member.Parameters.Count; ++i) {
2674 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2675 xpath.Append (member.Parameters [i]);
2676 xpath.Append ("\"");
2678 xpath.Append ("]/..");
2680 return xpath.ToString ();
2683 public static string GetXPathForMember (XPathNavigator member)
2685 StringBuilder xpath = new StringBuilder ();
2686 xpath.Append ("//Type[@FullName=\"")
2687 .Append (member.SelectSingleNode ("../../@FullName").Value)
2689 xpath.Append ("Members/Member[@MemberName=\"")
2690 .Append (member.SelectSingleNode ("@MemberName").Value)
2692 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2693 if (parameters.Count > 0) {
2694 xpath.Append ("/Parameters[count(Parameter) = ")
2695 .Append (parameters.Count);
2697 while (parameters.MoveNext ()) {
2699 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2700 xpath.Append (parameters.Current.Value);
2701 xpath.Append ("\"");
2703 xpath.Append ("]/..");
2705 return xpath.ToString ();
2708 public static string GetXPathForMember (MemberReference member)
2710 StringBuilder xpath = new StringBuilder ();
2711 xpath.Append ("//Type[@FullName=\"")
2712 .Append (member.DeclaringType.FullName)
2714 xpath.Append ("Members/Member[@MemberName=\"")
2715 .Append (GetMemberName (member))
2718 IList<ParameterDefinition> parameters = null;
2719 if (member is MethodDefinition)
2720 parameters = ((MethodDefinition) member).Parameters;
2721 else if (member is PropertyDefinition) {
2722 parameters = ((PropertyDefinition) member).Parameters;
2724 if (parameters != null && parameters.Count > 0) {
2725 xpath.Append ("/Parameters[count(Parameter) = ")
2726 .Append (parameters.Count);
2727 for (int i = 0; i < parameters.Count; ++i) {
2728 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2729 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2730 xpath.Append ("\"");
2732 xpath.Append ("]/..");
2734 return xpath.ToString ();
2738 static class CecilExtensions {
2739 public static string GetDeclaringType(this CustomAttribute attribute)
2741 var type = attribute.Constructor.DeclaringType;
2742 var typeName = type.FullName;
2744 string translatedType = NativeTypeManager.GetTranslatedName (type);
2745 return translatedType;
2748 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
2750 foreach (var c in type.Methods.Where (m => m.IsConstructor))
2751 yield return (MemberReference) c;
2752 foreach (var e in type.Events)
2753 yield return (MemberReference) e;
2754 foreach (var f in type.Fields)
2755 yield return (MemberReference) f;
2756 foreach (var m in type.Methods.Where (m => !m.IsConstructor))
2757 yield return (MemberReference) m;
2758 foreach (var t in type.NestedTypes)
2759 yield return (MemberReference) t;
2760 foreach (var p in type.Properties)
2761 yield return (MemberReference) p;
2764 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
2766 return GetMembers (type).Where (m => m.Name == member);
2769 public static MemberReference GetMember (this TypeDefinition type, string member)
2771 return GetMembers (type, member).EnsureZeroOrOne ();
2774 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2776 if (source.Count () > 1)
2777 throw new InvalidOperationException ("too many matches");
2778 return source.FirstOrDefault ();
2781 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2784 .Where (m => m.Name == method)
2785 .EnsureZeroOrOne ();
2788 public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
2790 TypeDefinition def = type as TypeDefinition;
2792 return new MemberReference [0];
2793 CustomAttribute defMemberAttr = def.CustomAttributes
2794 .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
2795 if (defMemberAttr == null)
2796 return new MemberReference [0];
2797 string name = (string) defMemberAttr.ConstructorArguments [0].Value;
2798 return def.Properties
2799 .Where (p => p.Name == name)
2800 .Select (p => (MemberReference) p);
2803 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2805 return assembly.Modules.SelectMany (md => md.GetAllTypes ());
2808 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2810 return GetTypes (assembly)
2811 .Where (td => td.FullName == type)
2812 .EnsureZeroOrOne ();
2815 public static bool IsGenericType (this TypeReference type)
2817 return type.GenericParameters.Count > 0;
2820 public static bool IsGenericMethod (this MethodReference method)
2822 return method.GenericParameters.Count > 0;
2825 public static MemberReference Resolve (this MemberReference member)
2827 FieldReference fr = member as FieldReference;
2829 return fr.Resolve ();
2830 MethodReference mr = member as MethodReference;
2832 return mr.Resolve ();
2833 TypeReference tr = member as TypeReference;
2835 return tr.Resolve ();
2836 PropertyReference pr = member as PropertyReference;
2839 EventReference er = member as EventReference;
2842 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2845 public static TypeReference GetUnderlyingType (this TypeDefinition type)
2849 return type.Fields.First (f => f.Name == "value__").FieldType;
2852 public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
2854 return self.Types.SelectMany (t => t.GetAllTypes ());
2857 static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
2861 if (!self.HasNestedTypes)
2864 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
2874 static class DocUtils {
2876 public static bool DoesNotHaveApiStyle(this XmlElement element, ApiStyle style) {
2877 string styleString = style.ToString ().ToLower ();
2878 string apistylevalue = element.GetAttribute ("apistyle");
2879 return apistylevalue != styleString || string.IsNullOrWhiteSpace(apistylevalue);
2881 public static bool HasApiStyle(this XmlElement element, ApiStyle style) {
2882 string styleString = style.ToString ().ToLower ();
2883 return element.GetAttribute ("apistyle") == styleString;
2885 public static void AddApiStyle(this XmlElement element, ApiStyle style) {
2886 string styleString = style.ToString ().ToLower ();
2887 var existingValue = element.GetAttribute ("apistyle");
2888 if (string.IsNullOrWhiteSpace (existingValue) || existingValue != styleString) {
2889 element.SetAttribute ("apistyle", styleString);
2893 public static bool IsExplicitlyImplemented (MethodDefinition method)
2895 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2898 public static string GetTypeDotMember (string name)
2900 int startType, startMethod;
2901 startType = startMethod = -1;
2902 for (int i = 0; i < name.Length; ++i) {
2903 if (name [i] == '.') {
2904 startType = startMethod;
2908 return name.Substring (startType+1);
2911 public static string GetMember (string name)
2913 int i = name.LastIndexOf ('.');
2916 return name.Substring (i+1);
2919 public static void GetInfoForExplicitlyImplementedMethod (
2920 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2924 if (method.Overrides.Count != 1)
2925 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2926 iface = method.Overrides [0].DeclaringType;
2927 ifaceMethod = method.Overrides [0];
2930 public static string GetPropertyName (PropertyDefinition pi)
2932 // Issue: (g)mcs-generated assemblies that explicitly implement
2933 // properties don't specify the full namespace, just the
2934 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2935 MethodDefinition method = pi.GetMethod;
2937 method = pi.SetMethod;
2938 if (!IsExplicitlyImplemented (method))
2941 // Need to determine appropriate namespace for this member.
2942 TypeReference iface;
2943 MethodReference ifaceMethod;
2944 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2945 return string.Join (".", new string[]{
2946 DocTypeFullMemberFormatter.Default.GetName (iface),
2947 GetMember (pi.Name)});
2950 public static string GetNamespace (TypeReference type)
2952 if (type.GetElementType ().IsNested)
2953 type = type.GetElementType ();
2954 while (type != null && type.IsNested)
2955 type = type.DeclaringType;
2957 return string.Empty;
2959 string typeNS = type.Namespace;
2961 // first, make sure this isn't a type reference to another assembly/module
2963 bool isInAssembly = MDocUpdater.IsInAssemblies(type.Module.Name);
2964 if (isInAssembly && !typeNS.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (type)) {
2965 typeNS = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typeNS);
2970 public static string PathCombine (string dir, string path)
2976 return Path.Combine (dir, path);
2979 public static bool IsExtensionMethod (MethodDefinition method)
2982 method.CustomAttributes
2983 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2984 && method.DeclaringType.CustomAttributes
2985 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
2988 public static bool IsDelegate (TypeDefinition type)
2990 TypeReference baseRef = type.BaseType;
2991 if (baseRef == null)
2993 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2994 baseRef.FullName == "System.MulticastDelegate";
2997 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2999 List<TypeReference> decls = new List<TypeReference> ();
3001 while (type.DeclaringType != null) {
3002 decls.Add (type.DeclaringType);
3003 type = type.DeclaringType;
3009 public static int GetGenericArgumentCount (TypeReference type)
3011 GenericInstanceType inst = type as GenericInstanceType;
3013 ? inst.GenericArguments.Count
3014 : type.GenericParameters.Count;
3017 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
3019 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
3020 List<TypeReference> userInterfaces = new List<TypeReference> ();
3021 foreach (TypeReference iface in type.Interfaces) {
3022 TypeReference lookup = iface.Resolve () ?? iface;
3023 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
3024 userInterfaces.Add (iface);
3026 return userInterfaces;
3029 private static string GetQualifiedTypeName (TypeReference type)
3031 return "[" + type.Scope.Name + "]" + type.FullName;
3034 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
3036 HashSet<string> inheritedInterfaces = new HashSet<string> ();
3037 Action<TypeDefinition> a = null;
3039 if (t == null) return;
3040 foreach (TypeReference r in t.Interfaces) {
3041 inheritedInterfaces.Add (GetQualifiedTypeName (r));
3045 TypeReference baseRef = type.BaseType;
3046 while (baseRef != null) {
3047 TypeDefinition baseDef = baseRef.Resolve ();
3048 if (baseDef != null) {
3050 baseRef = baseDef.BaseType;
3055 foreach (TypeReference r in type.Interfaces)
3057 return inheritedInterfaces;
3061 class DocsNodeInfo {
3062 public DocsNodeInfo (XmlElement node)
3067 public DocsNodeInfo (XmlElement node, TypeDefinition type)
3073 public DocsNodeInfo (XmlElement node, MemberReference member)
3076 SetMemberInfo (member);
3079 void SetType (TypeDefinition type)
3082 throw new ArgumentNullException ("type");
3084 GenericParameters = new List<GenericParameter> (type.GenericParameters);
3085 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
3086 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
3087 for (int i = 0; i < declTypes.Count - 1; ++i) {
3088 int remove = System.Math.Min (maxGenArgs,
3089 DocUtils.GetGenericArgumentCount (declTypes [i]));
3090 maxGenArgs -= remove;
3091 while (remove-- > 0)
3092 GenericParameters.RemoveAt (0);
3094 if (DocUtils.IsDelegate (type)) {
3095 Parameters = type.GetMethod("Invoke").Parameters;
3096 ReturnType = type.GetMethod("Invoke").ReturnType;
3097 ReturnIsReturn = true;
3101 void SetMemberInfo (MemberReference member)
3104 throw new ArgumentNullException ("member");
3105 ReturnIsReturn = true;
3109 if (member is MethodReference ) {
3110 MethodReference mr = (MethodReference) member;
3111 Parameters = mr.Parameters;
3112 if (mr.IsGenericMethod ()) {
3113 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
3116 else if (member is PropertyDefinition) {
3117 Parameters = ((PropertyDefinition) member).Parameters;
3120 if (member is MethodDefinition) {
3121 ReturnType = ((MethodDefinition) member).ReturnType;
3122 } else if (member is PropertyDefinition) {
3123 ReturnType = ((PropertyDefinition) member).PropertyType;
3124 ReturnIsReturn = false;
3127 // no remarks section for enum members
3128 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
3132 public TypeReference ReturnType;
3133 public List<GenericParameter> GenericParameters;
3134 public IList<ParameterDefinition> Parameters;
3135 public bool ReturnIsReturn;
3136 public XmlElement Node;
3137 public bool AddRemarks = true;
3138 public MemberReference Member;
3139 public TypeDefinition Type;
3141 public override string ToString ()
3143 return string.Format ("{0} - {1} - {2}", Type, Member, Node == null ? "no xml" : "with xml");
3147 class DocumentationEnumerator {
3149 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3151 return GetDocumentationTypes (assembly, forTypes, null);
3154 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3156 foreach (TypeDefinition type in assembly.GetTypes()) {
3157 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
3159 if (seen != null && seen.Contains (type.FullName))
3162 foreach (TypeDefinition nested in type.NestedTypes)
3163 yield return nested;
3167 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3169 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
3170 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
3171 oldmember.RemoveAttribute ("__monodocer-seen__");
3174 MemberReference m = GetMember (type, new DocumentationMember (oldmember));
3176 yield return new DocsNodeInfo (oldmember);
3179 yield return new DocsNodeInfo (oldmember, m);
3184 protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
3186 string membertype = member.MemberType;
3188 string returntype = member.ReturnType;
3190 string docName = member.MemberName;
3192 string[] docTypeParams = GetTypeParameters (docName);
3194 // If we're using 'magic types', then we might get false positives ... in those cases, we keep searching
3195 MemberReference likelyCandidate = null;
3197 // Loop through all members in this type with the same name
3198 var reflectedMembers = GetReflectionMembers (type, docName).ToArray ();
3199 foreach (MemberReference mi in reflectedMembers) {
3200 bool matchedMagicType = false;
3201 if (mi is TypeDefinition) continue;
3202 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
3204 if (MDocUpdater.IsPrivate (mi))
3207 IList<ParameterDefinition> pis = null;
3208 string[] typeParams = null;
3209 if (mi is MethodDefinition) {
3210 MethodDefinition mb = (MethodDefinition) mi;
3211 pis = mb.Parameters;
3212 if (docTypeParams != null && mb.IsGenericMethod ()) {
3213 IList<GenericParameter> args = mb.GenericParameters;
3214 if (args.Count == docTypeParams.Length) {
3215 typeParams = args.Select (p => p.Name).ToArray ();
3219 else if (mi is PropertyDefinition)
3220 pis = ((PropertyDefinition)mi).Parameters;
3222 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
3223 int pcount = pis == null ? 0 : pis.Count;
3224 if (mcount != pcount)
3227 MethodDefinition mDef = mi as MethodDefinition;
3228 if (mDef != null && !mDef.IsConstructor) {
3229 // Casting operators can overload based on return type.
3230 string rtype = GetReplacedString (
3231 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
3232 typeParams, docTypeParams);
3233 string originalRType = rtype;
3234 if (MDocUpdater.SwitchingToMagicTypes) {
3235 rtype = NativeTypeManager.ConvertFromNativeType (rtype);
3238 if ((returntype != rtype && originalRType == rtype) ||
3239 (MDocUpdater.SwitchingToMagicTypes && returntype != originalRType && returntype != rtype && originalRType != rtype)) {
3243 if (originalRType != rtype)
3244 matchedMagicType = true;
3250 for (int i = 0; i < pis.Count; i++) {
3251 string paramType = GetReplacedString (
3252 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
3253 typeParams, docTypeParams);
3255 // if magictypes, replace paramType to "classic value" ... so the comparison works
3256 string originalParamType = paramType;
3257 if (MDocUpdater.SwitchingToMagicTypes) {
3258 paramType = NativeTypeManager.ConvertFromNativeType (paramType);
3261 string xmlMemberType = member.Parameters [i];
3262 if ((!paramType.Equals(xmlMemberType) && paramType.Equals(originalParamType)) ||
3263 (MDocUpdater.SwitchingToMagicTypes && !originalParamType.Equals(xmlMemberType) && !paramType.Equals(xmlMemberType) && !paramType.Equals(originalParamType))) {
3265 // did not match ... if we're dropping the namespace, and the paramType has the dropped
3266 // namespace, we should see if it matches when added
3267 bool stillDoesntMatch = true;
3268 if (MDocUpdater.HasDroppedNamespace(type) && paramType.StartsWith (MDocUpdater.droppedNamespace)) {
3269 string withDroppedNs = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, xmlMemberType);
3271 stillDoesntMatch = withDroppedNs != paramType;
3274 if (stillDoesntMatch) {
3280 if (originalParamType != paramType)
3281 matchedMagicType = true;
3283 if (!good) continue;
3285 if (MDocUpdater.SwitchingToMagicTypes && likelyCandidate == null && matchedMagicType) {
3286 // 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
3287 likelyCandidate = mi;
3294 return likelyCandidate;
3297 static string[] GetTypeParameters (string docName)
3299 if (docName [docName.Length-1] != '>')
3301 StringList types = new StringList ();
3302 int endToken = docName.Length-2;
3303 int i = docName.Length-2;
3305 if (docName [i] == ',' || docName [i] == '<') {
3306 types.Add (docName.Substring (i + 1, endToken - i));
3309 if (docName [i] == '<')
3314 return types.ToArray ();
3317 protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
3319 // In case of dropping the namespace, we have to remove the dropped NS
3320 // so that docName will match what's in the assembly/type
3321 if (MDocUpdater.HasDroppedNamespace (type) && docName.StartsWith(MDocUpdater.droppedNamespace + ".")) {
3322 int droppedNsLength = MDocUpdater.droppedNamespace.Length;
3323 docName = docName.Substring (droppedNsLength + 1, docName.Length - droppedNsLength - 1);
3326 // need to worry about 4 forms of //@MemberName values:
3327 // 1. "Normal" (non-generic) member names: GetEnumerator
3329 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
3330 // - try as-is, and try type.member (due to "kludge" for property
3332 // 3. "Normal" Generic member names: Sort<T> (CSC)
3333 // - need to remove generic parameters --> "Sort"
3334 // 4. Explicitly-implemented interface members for generic interfaces:
3335 // -- System.Collections.Generic.IEnumerable<T>.Current
3336 // - Try as-is, and try type.member, *keeping* the generic parameters.
3337 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
3338 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
3339 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
3340 // this as (1) or (2).
3341 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
3343 foreach (MemberReference mi in type.GetMembers (docName))
3345 if (CountChars (docName, '.') > 0)
3346 // might be a property; try only type.member instead of
3347 // namespace.type.member.
3348 foreach (MemberReference mi in
3349 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
3356 int startLt, startType, startMethod;
3357 startLt = startType = startMethod = -1;
3358 for (int i = 0; i < docName.Length; ++i) {
3359 switch (docName [i]) {
3368 if (numLt == 0 && (i + 1) < docName.Length)
3369 // there's another character in docName, so this <...> sequence is
3370 // probably part of a generic type -- case 4.
3374 startType = startMethod;
3380 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
3382 foreach (MemberReference mi in type.GetMembers (refName))
3386 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
3389 // If we _still_ haven't found it, we've hit another generic naming issue:
3390 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
3391 // explicitly-implemented METHOD names (not properties), e.g.
3392 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
3393 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
3394 // which the XML docs will contain.
3396 // Alas, we can't derive the Mono name from docName, so we need to iterate
3397 // over all member names, convert them into CSC format, and compare... :-(
3400 foreach (MemberReference mi in type.GetMembers ()) {
3401 if (MDocUpdater.GetMemberName (mi) == docName)
3406 static string GetReplacedString (string typeName, string[] from, string[] to)
3410 for (int i = 0; i < from.Length; ++i)
3411 typeName = typeName.Replace (from [i], to [i]);
3415 private static int CountChars (string s, char c)
3418 for (int i = 0; i < s.Length; ++i) {
3426 class EcmaDocumentationEnumerator : DocumentationEnumerator {
3431 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
3434 this.ecmadocs = ecmaDocs;
3437 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3439 HashSet<string> seen = new HashSet<string> ();
3440 return GetDocumentationTypes (assembly, forTypes, seen)
3441 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
3444 new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3447 while (ecmadocs.Read ()) {
3448 switch (ecmadocs.Name) {
3450 if (typeDepth == -1)
3451 typeDepth = ecmadocs.Depth;
3452 if (ecmadocs.NodeType != XmlNodeType.Element)
3454 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
3456 string typename = ecmadocs.GetAttribute ("FullName");
3457 string typename2 = MDocUpdater.GetTypeFileName (typename);
3458 if (forTypes != null &&
3459 forTypes.BinarySearch (typename) < 0 &&
3460 typename != typename2 &&
3461 forTypes.BinarySearch (typename2) < 0)
3464 if ((t = assembly.GetType (typename)) == null &&
3465 (t = assembly.GetType (typename2)) == null)
3467 seen.Add (typename);
3468 if (typename != typename2)
3469 seen.Add (typename2);
3470 Console.WriteLine (" Import: {0}", t.FullName);
3471 if (ecmadocs.Name != "Docs") {
3472 int depth = ecmadocs.Depth;
3473 while (ecmadocs.Read ()) {
3474 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
3478 if (!ecmadocs.IsStartElement ("Docs"))
3479 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
3489 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3491 return GetMembers (basefile, type)
3492 .Concat (base.GetDocumentationMembers (basefile, type));
3495 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
3497 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
3500 if (ecmadocs.IsEmptyElement)
3503 int membersDepth = ecmadocs.Depth;
3505 while (go && ecmadocs.Read ()) {
3506 switch (ecmadocs.Name) {
3508 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
3510 DocumentationMember dm = new DocumentationMember (ecmadocs);
3512 string xp = MDocUpdater.GetXPathForMember (dm);
3513 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
3515 if (oldmember == null) {
3516 m = GetMember (type, dm);
3518 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3519 type.FullName, dm.MemberSignatures ["C#"]);
3520 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
3523 // oldmember lookup may have failed due to type parameter renames.
3525 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
3526 if (oldmember == null) {
3527 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
3528 oldmember = basefile.CreateElement ("Member");
3529 oldmember.SetAttribute ("MemberName", dm.MemberName);
3530 members.AppendChild (oldmember);
3531 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
3532 XmlElement ms = basefile.CreateElement ("MemberSignature");
3533 ms.SetAttribute ("Language", key);
3534 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
3535 oldmember.AppendChild (ms);
3537 oldmember.SetAttribute ("__monodocer-seen__", "true");
3538 Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
3543 m = GetMember (type, new DocumentationMember (oldmember));
3545 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3546 type.FullName, dm.MemberSignatures ["C#"]);
3549 oldmember.SetAttribute ("__monodocer-seen__", "true");
3551 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
3552 if (ecmadocs.Name != "Docs")
3553 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
3558 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
3567 abstract class DocumentationImporter {
3569 public abstract void ImportDocumentation (DocsNodeInfo info);
3572 class MsxdocDocumentationImporter : DocumentationImporter {
3574 XmlDocument slashdocs;
3576 public MsxdocDocumentationImporter (string file)
3578 var xml = File.ReadAllText (file);
3580 // Ensure Unix line endings
3581 xml = xml.Replace ("\r", "");
3583 slashdocs = new XmlDocument();
3584 slashdocs.LoadXml (xml);
3587 public override void ImportDocumentation (DocsNodeInfo info)
3589 XmlNode elem = GetDocs (info.Member ?? info.Type);
3594 XmlElement e = info.Node;
3596 if (elem.SelectSingleNode("summary") != null)
3597 MDocUpdater.ClearElement(e, "summary");
3598 if (elem.SelectSingleNode("remarks") != null)
3599 MDocUpdater.ClearElement(e, "remarks");
3600 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3601 MDocUpdater.ClearElement(e, "value");
3602 MDocUpdater.ClearElement(e, "returns");
3605 foreach (XmlNode child in elem.ChildNodes) {
3606 switch (child.Name) {
3609 XmlAttribute name = child.Attributes ["name"];
3612 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3614 p2.InnerXml = child.InnerXml;
3617 // Occasionally XML documentation will use <returns/> on
3618 // properties, so let's try to normalize things.
3621 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3622 v.InnerXml = child.InnerXml;
3628 case "permission": {
3629 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3632 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3634 a = e.OwnerDocument.CreateElement (child.Name);
3635 a.SetAttribute ("cref", cref.Value);
3638 a.InnerXml = child.InnerXml;
3642 XmlAttribute cref = child.Attributes ["cref"];
3645 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3647 a = e.OwnerDocument.CreateElement ("altmember");
3648 a.SetAttribute ("cref", cref.Value);
3655 if (child.NodeType == XmlNodeType.Element &&
3656 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3659 MDocUpdater.CopyNode (child, e);
3666 private XmlNode GetDocs (MemberReference member)
3668 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3669 if (slashdocsig != null)
3670 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3675 class EcmaDocumentationImporter : DocumentationImporter {
3679 public EcmaDocumentationImporter (XmlReader ecmaDocs)
3681 this.ecmadocs = ecmaDocs;
3684 public override void ImportDocumentation (DocsNodeInfo info)
3686 if (!ecmadocs.IsStartElement ("Docs")) {
3690 XmlElement e = info.Node;
3692 int depth = ecmadocs.Depth;
3693 ecmadocs.ReadStartElement ("Docs");
3694 while (ecmadocs.Read ()) {
3695 if (ecmadocs.Name == "Docs") {
3696 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3699 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3701 if (!ecmadocs.IsStartElement ())
3703 switch (ecmadocs.Name) {
3706 string name = ecmadocs.GetAttribute ("name");
3709 XmlNode doc = e.SelectSingleNode (
3710 ecmadocs.Name + "[@name='" + name + "']");
3711 string value = ecmadocs.ReadInnerXml ();
3713 doc.InnerXml = value.Replace ("\r", "");
3720 string name = ecmadocs.Name;
3721 string cref = ecmadocs.GetAttribute ("cref");
3724 XmlNode doc = e.SelectSingleNode (
3725 ecmadocs.Name + "[@cref='" + cref + "']");
3726 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3728 doc.InnerXml = value;
3730 XmlElement n = e.OwnerDocument.CreateElement (name);
3731 n.SetAttribute ("cref", cref);
3738 string name = ecmadocs.Name;
3739 string xpath = ecmadocs.Name;
3740 StringList attributes = new StringList (ecmadocs.AttributeCount);
3741 if (ecmadocs.MoveToFirstAttribute ()) {
3743 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3744 } while (ecmadocs.MoveToNextAttribute ());
3745 ecmadocs.MoveToContent ();
3747 if (attributes.Count > 0) {
3748 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3750 XmlNode doc = e.SelectSingleNode (xpath);
3751 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3753 doc.InnerXml = value;
3756 XmlElement n = e.OwnerDocument.CreateElement (name);
3758 foreach (string a in attributes) {
3759 int eq = a.IndexOf ('=');
3760 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3771 class DocumentationMember {
3772 public StringToStringMap MemberSignatures = new StringToStringMap ();
3773 public string ReturnType;
3774 public StringList Parameters;
3775 public string MemberName;
3776 public string MemberType;
3778 public DocumentationMember (XmlReader reader)
3780 MemberName = reader.GetAttribute ("MemberName");
3781 int depth = reader.Depth;
3783 StringList p = new StringList ();
3785 if (reader.NodeType != XmlNodeType.Element)
3788 bool shouldUse = true;
3790 string apistyle = reader.GetAttribute ("apistyle");
3791 shouldUse = string.IsNullOrWhiteSpace(apistyle) || apistyle == "classic"; // only use this tag if it's an 'classic' style node
3793 catch (Exception ex) {}
3794 switch (reader.Name) {
3795 case "MemberSignature":
3797 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3801 MemberType = reader.ReadElementString ();
3804 if (reader.Depth == depth + 2 && shouldUse)
3805 ReturnType = reader.ReadElementString ();
3808 if (reader.Depth == depth + 2 && shouldUse)
3809 p.Add (reader.GetAttribute ("Type"));
3812 if (reader.Depth == depth + 1)
3816 } while (go && reader.Read () && reader.Depth >= depth);
3822 public DocumentationMember (XmlNode node)
3824 MemberName = node.Attributes ["MemberName"].Value;
3825 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3826 XmlAttribute l = n.Attributes ["Language"];
3827 XmlAttribute v = n.Attributes ["Value"];
3828 XmlAttribute apistyle = n.Attributes ["apistyle"];
3829 bool shouldUse = apistyle == null || apistyle.Value == "classic";
3830 if (l != null && v != null && shouldUse)
3831 MemberSignatures [l.Value] = v.Value;
3833 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3834 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType[not(@apistyle) or @apistyle='classic']");
3836 ReturnType = rt.InnerText;
3837 XmlNodeList p = node.SelectNodes ("Parameters/Parameter[not(@apistyle) or @apistyle='classic']");
3839 Parameters = new StringList (p.Count);
3840 for (int i = 0; i < p.Count; ++i)
3841 Parameters.Add (p [i].Attributes ["Type"].Value);
3846 public class DynamicParserContext {
3847 public ReadOnlyCollection<bool> TransformFlags;
3848 public int TransformIndex;
3850 public DynamicParserContext (ICustomAttributeProvider provider)
3853 if (provider.HasCustomAttributes &&
3854 (da = (provider.CustomAttributes.Cast<CustomAttribute>()
3855 .SingleOrDefault (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))) != null) {
3856 CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
3857 ? new CustomAttributeArgument [0]
3858 : (CustomAttributeArgument[]) da.ConstructorArguments [0].Value;
3860 TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool) t.Value).ToArray());
3865 public enum MemberFormatterState {
3867 WithinGenericTypeParameters,
3870 public abstract class MemberFormatter {
3872 public virtual string Language {
3876 public string GetName (MemberReference member)
3878 return GetName (member, null);
3881 public virtual string GetName (MemberReference member, DynamicParserContext context)
3883 TypeReference type = member as TypeReference;
3885 return GetTypeName (type, context);
3886 MethodReference method = member as MethodReference;
3887 if (method != null && method.Name == ".ctor") // method.IsConstructor
3888 return GetConstructorName (method);
3890 return GetMethodName (method);
3891 PropertyReference prop = member as PropertyReference;
3893 return GetPropertyName (prop);
3894 FieldReference field = member as FieldReference;
3896 return GetFieldName (field);
3897 EventReference e = member as EventReference;
3899 return GetEventName (e);
3900 throw new NotSupportedException ("Can't handle: " +
3901 (member == null ? "null" : member.GetType().ToString()));
3904 protected virtual string GetTypeName (TypeReference type)
3906 return GetTypeName (type, null);
3909 protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
3912 throw new ArgumentNullException ("type");
3913 return _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
3916 protected virtual char[] ArrayDelimeters {
3917 get {return new char[]{'[', ']'};}
3920 protected virtual MemberFormatterState MemberFormatterState { get; set; }
3922 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3924 if (type is ArrayType) {
3925 TypeSpecification spec = type as TypeSpecification;
3926 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
3927 return AppendArrayModifiers (buf, (ArrayType) type);
3929 if (type is ByReferenceType) {
3930 return AppendRefTypeName (buf, type, context);
3932 if (type is PointerType) {
3933 return AppendPointerTypeName (buf, type, context);
3935 if (type is GenericParameter) {
3936 return AppendTypeName (buf, type, context);
3938 AppendNamespace (buf, type);
3939 GenericInstanceType genInst = type as GenericInstanceType;
3940 if (type.GenericParameters.Count == 0 &&
3941 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3942 return AppendFullTypeName (buf, type, context);
3944 return AppendGenericType (buf, type, context);
3947 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3949 string ns = DocUtils.GetNamespace (type);
3950 if (ns != null && ns.Length > 0)
3951 buf.Append (ns).Append ('.');
3955 protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3957 if (type.DeclaringType != null)
3958 AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
3959 return AppendTypeName (buf, type, context);
3962 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3964 if (context != null)
3965 context.TransformIndex++;
3966 return AppendTypeName (buf, type.Name);
3969 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3971 int n = typename.IndexOf ("`");
3973 return buf.Append (typename.Substring (0, n));
3974 return buf.Append (typename);
3977 protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
3979 buf.Append (ArrayDelimeters [0]);
3980 int rank = array.Rank;
3982 buf.Append (new string (',', rank-1));
3983 return buf.Append (ArrayDelimeters [1]);
3986 protected virtual string RefTypeModifier {
3990 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3992 TypeSpecification spec = type as TypeSpecification;
3993 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
3994 .Append (RefTypeModifier);
3997 protected virtual string PointerModifier {
4001 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4003 TypeSpecification spec = type as TypeSpecification;
4004 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4005 .Append (PointerModifier);
4008 protected virtual char[] GenericTypeContainer {
4009 get {return new char[]{'<', '>'};}
4012 protected virtual char NestedTypeSeparator {
4016 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4018 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4019 type is GenericInstanceType ? type.GetElementType () : type);
4020 List<TypeReference> genArgs = GetGenericArguments (type);
4023 bool insertNested = false;
4024 foreach (var decl in decls) {
4025 TypeReference declDef = decl.Resolve () ?? decl;
4027 buf.Append (NestedTypeSeparator);
4029 insertNested = true;
4030 AppendTypeName (buf, declDef, context);
4031 int ac = DocUtils.GetGenericArgumentCount (declDef);
4035 buf.Append (GenericTypeContainer [0]);
4036 var origState = MemberFormatterState;
4037 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4038 _AppendTypeName (buf, genArgs [argIdx++], context);
4039 for (int i = 1; i < c; ++i) {
4040 _AppendTypeName (buf.Append (","), genArgs [argIdx++], context);
4042 MemberFormatterState = origState;
4043 buf.Append (GenericTypeContainer [1]);
4049 protected List<TypeReference> GetGenericArguments (TypeReference type)
4051 var args = new List<TypeReference> ();
4052 GenericInstanceType inst = type as GenericInstanceType;
4054 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
4056 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
4060 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4065 protected virtual string GetConstructorName (MethodReference constructor)
4067 return constructor.Name;
4070 protected virtual string GetMethodName (MethodReference method)
4075 protected virtual string GetPropertyName (PropertyReference property)
4077 return property.Name;
4080 protected virtual string GetFieldName (FieldReference field)
4085 protected virtual string GetEventName (EventReference e)
4090 public virtual string GetDeclaration (MemberReference member)
4093 throw new ArgumentNullException ("member");
4094 TypeDefinition type = member as TypeDefinition;
4096 return GetTypeDeclaration (type);
4097 MethodDefinition method = member as MethodDefinition;
4098 if (method != null && method.IsConstructor)
4099 return GetConstructorDeclaration (method);
4101 return GetMethodDeclaration (method);
4102 PropertyDefinition prop = member as PropertyDefinition;
4104 return GetPropertyDeclaration (prop);
4105 FieldDefinition field = member as FieldDefinition;
4107 return GetFieldDeclaration (field);
4108 EventDefinition e = member as EventDefinition;
4110 return GetEventDeclaration (e);
4111 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
4114 protected virtual string GetTypeDeclaration (TypeDefinition type)
4117 throw new ArgumentNullException ("type");
4118 StringBuilder buf = new StringBuilder (type.Name.Length);
4119 _AppendTypeName (buf, type, null);
4120 AppendGenericTypeConstraints (buf, type);
4121 return buf.ToString ();
4124 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
4126 return GetConstructorName (constructor);
4129 protected virtual string GetMethodDeclaration (MethodDefinition method)
4131 if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
4132 ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
4135 // Special signature for destructors.
4136 if (method.Name == "Finalize" && method.Parameters.Count == 0)
4137 return GetFinalizerName (method);
4139 StringBuilder buf = new StringBuilder ();
4141 AppendVisibility (buf, method);
4142 if (buf.Length == 0 &&
4143 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
4146 AppendModifiers (buf, method);
4148 if (buf.Length != 0)
4150 buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
4152 AppendMethodName (buf, method);
4153 AppendGenericMethod (buf, method).Append (" ");
4154 AppendParameters (buf, method, method.Parameters);
4155 AppendGenericMethodConstraints (buf, method);
4156 return buf.ToString ();
4159 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4161 return buf.Append (method.Name);
4164 protected virtual string GetFinalizerName (MethodDefinition method)
4169 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4174 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4179 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4184 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4189 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4194 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
4196 return GetPropertyName (property);
4199 protected virtual string GetFieldDeclaration (FieldDefinition field)
4201 return GetFieldName (field);
4204 protected virtual string GetEventDeclaration (EventDefinition e)
4206 return GetEventName (e);
4210 class ILFullMemberFormatter : MemberFormatter {
4212 public override string Language {
4213 get {return "ILAsm";}
4216 protected override char NestedTypeSeparator {
4222 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4224 if (GetBuiltinType (type.FullName) != null)
4226 string ns = DocUtils.GetNamespace (type);
4227 if (ns != null && ns.Length > 0) {
4228 if (type.IsValueType)
4229 buf.Append ("valuetype ");
4231 buf.Append ("class ");
4232 buf.Append (ns).Append ('.');
4237 protected static string GetBuiltinType (string t)
4240 case "System.Byte": return "unsigned int8";
4241 case "System.SByte": return "int8";
4242 case "System.Int16": return "int16";
4243 case "System.Int32": return "int32";
4244 case "System.Int64": return "int64";
4245 case "System.IntPtr": return "native int";
4247 case "System.UInt16": return "unsigned int16";
4248 case "System.UInt32": return "unsigned int32";
4249 case "System.UInt64": return "unsigned int64";
4250 case "System.UIntPtr": return "native unsigned int";
4252 case "System.Single": return "float32";
4253 case "System.Double": return "float64";
4254 case "System.Boolean": return "bool";
4255 case "System.Char": return "char";
4256 case "System.Void": return "void";
4257 case "System.String": return "string";
4258 case "System.Object": return "object";
4263 protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
4265 return buf.Append (typename);
4268 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4270 if (type is GenericParameter) {
4271 AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
4275 string s = GetBuiltinType (type.FullName);
4277 return buf.Append (s);
4279 return base.AppendTypeName (buf, type, context);
4282 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
4284 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
4285 return buf.Append (type.Owner is TypeReference ? "!" : "!!");
4287 GenericParameterAttributes attrs = type.Attributes;
4288 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
4289 buf.Append ("class ");
4290 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
4291 buf.Append ("struct ");
4292 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
4293 buf.Append (".ctor ");
4294 IList<TypeReference> constraints = type.Constraints;
4295 MemberFormatterState = 0;
4296 if (constraints.Count > 0) {
4297 var full = new ILFullMemberFormatter ();
4298 buf.Append ("(").Append (full.GetName (constraints [0]));
4299 for (int i = 1; i < constraints.Count; ++i) {
4300 buf.Append (", ").Append (full.GetName (constraints [i]));
4304 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4306 if ((attrs & GenericParameterAttributes.Covariant) != 0)
4308 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
4313 protected override string GetTypeDeclaration (TypeDefinition type)
4315 string visibility = GetTypeVisibility (type.Attributes);
4316 if (visibility == null)
4319 StringBuilder buf = new StringBuilder ();
4321 buf.Append (".class ");
4323 buf.Append ("nested ");
4324 buf.Append (visibility).Append (" ");
4325 if (type.IsInterface)
4326 buf.Append ("interface ");
4327 if (type.IsSequentialLayout)
4328 buf.Append ("sequential ");
4329 if (type.IsAutoLayout)
4330 buf.Append ("auto ");
4331 if (type.IsAnsiClass)
4332 buf.Append ("ansi ");
4333 if (type.IsAbstract)
4334 buf.Append ("abstract ");
4335 if (type.IsSerializable)
4336 buf.Append ("serializable ");
4338 buf.Append ("sealed ");
4339 if (type.IsBeforeFieldInit)
4340 buf.Append ("beforefieldinit ");
4341 var state = MemberFormatterState;
4342 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4343 buf.Append (GetName (type));
4344 MemberFormatterState = state;
4345 var full = new ILFullMemberFormatter ();
4346 if (type.BaseType != null) {
4347 buf.Append (" extends ");
4348 if (type.BaseType.FullName == "System.Object")
4349 buf.Append ("System.Object");
4351 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
4354 foreach (var name in type.Interfaces
4355 .Select (i => full.GetName (i))
4356 .OrderBy (n => n)) {
4358 buf.Append (" implements ");
4367 return buf.ToString ();
4370 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4372 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4373 type is GenericInstanceType ? type.GetElementType () : type);
4375 foreach (var decl in decls) {
4376 TypeReference declDef = decl.Resolve () ?? decl;
4378 buf.Append (NestedTypeSeparator);
4381 AppendTypeName (buf, declDef, context);
4385 foreach (TypeReference arg in GetGenericArguments (type)) {
4389 _AppendTypeName (buf, arg, context);
4395 static string GetTypeVisibility (TypeAttributes ta)
4397 switch (ta & TypeAttributes.VisibilityMask) {
4398 case TypeAttributes.Public:
4399 case TypeAttributes.NestedPublic:
4402 case TypeAttributes.NestedFamily:
4403 case TypeAttributes.NestedFamORAssem:
4411 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4413 return GetMethodDeclaration (constructor);
4416 protected override string GetMethodDeclaration (MethodDefinition method)
4418 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
4421 var buf = new StringBuilder ();
4422 buf.Append (".method ");
4423 AppendVisibility (buf, method);
4424 if (method.IsStatic)
4425 buf.Append ("static ");
4426 if (method.IsHideBySig)
4427 buf.Append ("hidebysig ");
4428 if (method.IsPInvokeImpl) {
4429 var info = method.PInvokeInfo;
4430 buf.Append ("pinvokeimpl (\"")
4431 .Append (info.Module.Name)
4432 .Append ("\" as \"")
4433 .Append (info.EntryPoint)
4435 if (info.IsCharSetAuto)
4436 buf.Append (" auto");
4437 if (info.IsCharSetUnicode)
4438 buf.Append (" unicode");
4439 if (info.IsCharSetAnsi)
4440 buf.Append (" ansi");
4441 if (info.IsCallConvCdecl)
4442 buf.Append (" cdecl");
4443 if (info.IsCallConvStdCall)
4444 buf.Append (" stdcall");
4445 if (info.IsCallConvWinapi)
4446 buf.Append (" winapi");
4447 if (info.IsCallConvThiscall)
4448 buf.Append (" thiscall");
4449 if (info.SupportsLastError)
4450 buf.Append (" lasterr");
4453 if (method.IsSpecialName)
4454 buf.Append ("specialname ");
4455 if (method.IsRuntimeSpecialName)
4456 buf.Append ("rtspecialname ");
4457 if (method.IsNewSlot)
4458 buf.Append ("newslot ");
4459 if (method.IsVirtual)
4460 buf.Append ("virtual ");
4461 if (!method.IsStatic)
4462 buf.Append ("instance ");
4463 _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
4465 .Append (method.Name);
4466 if (method.IsGenericMethod ()) {
4467 var state = MemberFormatterState;
4468 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4469 IList<GenericParameter> args = method.GenericParameters;
4470 if (args.Count > 0) {
4472 _AppendTypeName (buf, args [0], null);
4473 for (int i = 1; i < args.Count; ++i)
4474 _AppendTypeName (buf.Append (", "), args [i], null);
4477 MemberFormatterState = state;
4482 for (int i = 0; i < method.Parameters.Count; ++i) {
4486 _AppendTypeName (buf, method.Parameters [i].ParameterType, new DynamicParserContext (method.Parameters [i]));
4488 buf.Append (method.Parameters [i].Name);
4492 buf.Append (" cil");
4493 if (method.IsRuntime)
4494 buf.Append (" runtime");
4495 if (method.IsManaged)
4496 buf.Append (" managed");
4498 return buf.ToString ();
4501 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4503 if (DocUtils.IsExplicitlyImplemented (method)) {
4504 TypeReference iface;
4505 MethodReference ifaceMethod;
4506 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4507 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4509 .Append (ifaceMethod.Name);
4511 return base.AppendMethodName (buf, method);
4514 protected override string RefTypeModifier {
4518 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4520 if (method.IsPublic)
4521 return buf.Append ("public ");
4522 if (method.IsFamilyAndAssembly)
4523 return buf.Append ("familyandassembly");
4524 if (method.IsFamilyOrAssembly)
4525 return buf.Append ("familyorassembly");
4526 if (method.IsFamily)
4527 return buf.Append ("family");
4531 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4533 string modifiers = String.Empty;
4534 if (method.IsStatic) modifiers += " static";
4535 if (method.IsVirtual && !method.IsAbstract) {
4536 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4537 else modifiers += " override";
4539 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4540 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4541 if (method.IsFinal) modifiers += " sealed";
4542 if (modifiers == " virtual sealed") modifiers = "";
4544 return buf.Append (modifiers);
4547 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4549 if (method.IsGenericMethod ()) {
4550 IList<GenericParameter> args = method.GenericParameters;
4551 if (args.Count > 0) {
4553 buf.Append (args [0].Name);
4554 for (int i = 1; i < args.Count; ++i)
4555 buf.Append (",").Append (args [i].Name);
4562 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4564 return AppendParameters (buf, method, parameters, '(', ')');
4567 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4571 if (parameters.Count > 0) {
4572 if (DocUtils.IsExtensionMethod (method))
4573 buf.Append ("this ");
4574 AppendParameter (buf, parameters [0]);
4575 for (int i = 1; i < parameters.Count; ++i) {
4577 AppendParameter (buf, parameters [i]);
4581 return buf.Append (end);
4584 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4586 if (parameter.ParameterType is ByReferenceType) {
4587 if (parameter.IsOut)
4588 buf.Append ("out ");
4590 buf.Append ("ref ");
4592 buf.Append (GetName (parameter.ParameterType)).Append (" ");
4593 return buf.Append (parameter.Name);
4596 protected override string GetPropertyDeclaration (PropertyDefinition property)
4598 MethodDefinition gm = null, sm = null;
4600 string get_visible = null;
4601 if ((gm = property.GetMethod) != null &&
4602 (DocUtils.IsExplicitlyImplemented (gm) ||
4603 (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
4604 get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
4605 string set_visible = null;
4606 if ((sm = property.SetMethod) != null &&
4607 (DocUtils.IsExplicitlyImplemented (sm) ||
4608 (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
4609 set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
4611 if ((set_visible == null) && (get_visible == null))
4614 StringBuilder buf = new StringBuilder ()
4615 .Append (".property ");
4616 if (!(gm ?? sm).IsStatic)
4617 buf.Append ("instance ");
4618 _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
4619 buf.Append (' ').Append (property.Name);
4620 if (!property.HasParameters || property.Parameters.Count == 0)
4621 return buf.ToString ();
4625 foreach (ParameterDefinition p in property.Parameters) {
4629 _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
4633 return buf.ToString ();
4636 protected override string GetFieldDeclaration (FieldDefinition field)
4638 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4639 if (declType.IsEnum && field.Name == "value__")
4640 return null; // This member of enums aren't documented.
4642 StringBuilder buf = new StringBuilder ();
4643 AppendFieldVisibility (buf, field);
4644 if (buf.Length == 0)
4647 buf.Insert (0, ".field ");
4650 buf.Append ("static ");
4651 if (field.IsInitOnly)
4652 buf.Append ("initonly ");
4653 if (field.IsLiteral)
4654 buf.Append ("literal ");
4655 _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
4656 buf.Append (' ').Append (field.Name);
4657 AppendFieldValue (buf, field);
4659 return buf.ToString ();
4662 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4665 return buf.Append ("public ");
4666 if (field.IsFamilyAndAssembly)
4667 return buf.Append ("familyandassembly ");
4668 if (field.IsFamilyOrAssembly)
4669 return buf.Append ("familyorassembly ");
4671 return buf.Append ("family ");
4675 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4677 // enums have a value__ field, which we ignore
4678 if (field.DeclaringType.IsGenericType ())
4680 if (field.HasConstant && field.IsLiteral) {
4683 val = field.Constant;
4688 buf.Append (" = ").Append ("null");
4689 else if (val is Enum)
4691 .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4693 .Append (val.ToString ())
4695 else if (val is IFormattable) {
4696 string value = ((IFormattable)val).ToString();
4699 buf.Append ("\"" + value + "\"");
4701 buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4710 protected override string GetEventDeclaration (EventDefinition e)
4712 StringBuilder buf = new StringBuilder ();
4713 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4718 buf.Append (".event ")
4719 .Append (GetName (e.EventType))
4723 return buf.ToString ();
4727 class ILMemberFormatter : ILFullMemberFormatter {
4728 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4734 class ILNativeTypeMemberFormatter : ILFullMemberFormatter {
4735 protected static string _GetBuiltinType (string t)
4737 //string moddedType = base.GetBuiltinType (t);
4739 //return moddedType;
4743 class CSharpNativeTypeMemberFormatter : CSharpFullMemberFormatter {
4744 protected override string GetCSharpType (string t) {
4745 string moddedType = base.GetCSharpType (t);
4747 switch (moddedType) {
4748 case "int": return "nint";
4753 case "System.Drawing.SizeF":
4754 return "CoreGraphics.CGSize";
4755 case "System.Drawing.PointF":
4756 return "CoreGraphics.CGPoint";
4757 case "System.Drawing.RectangleF":
4758 return "CoreGraphics.CGPoint";
4764 class CSharpFullMemberFormatter : MemberFormatter {
4766 public override string Language {
4770 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4773 string ns = DocUtils.GetNamespace (type);
4774 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
4775 buf.Append (ns).Append ('.');
4779 protected virtual string GetCSharpType (string t)
4782 case "System.Byte": return "byte";
4783 case "System.SByte": return "sbyte";
4784 case "System.Int16": return "short";
4785 case "System.Int32": return "int";
4786 case "System.Int64": return "long";
4788 case "System.UInt16": return "ushort";
4789 case "System.UInt32": return "uint";
4790 case "System.UInt64": return "ulong";
4792 case "System.Single": return "float";
4793 case "System.Double": return "double";
4794 case "System.Decimal": return "decimal";
4795 case "System.Boolean": return "bool";
4796 case "System.Char": return "char";
4797 case "System.Void": return "void";
4798 case "System.String": return "string";
4799 case "System.Object": return "object";
4804 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4806 if (context != null && context.TransformFlags != null &&
4807 (context.TransformFlags.Count == 0 || context.TransformFlags [context.TransformIndex])) {
4808 context.TransformIndex++;
4809 return buf.Append ("dynamic");
4812 if (type is GenericParameter)
4813 return AppendGenericParameterConstraints (buf, (GenericParameter) type, context).Append (type.Name);
4814 string t = type.FullName;
4815 if (!t.StartsWith ("System.")) {
4816 return base.AppendTypeName (buf, type, context);
4819 string s = GetCSharpType (t);
4821 if (context != null)
4822 context.TransformIndex++;
4823 return buf.Append (s);
4826 return base.AppendTypeName (buf, type, context);
4829 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
4831 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
4833 GenericParameterAttributes attrs = type.Attributes;
4834 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
4835 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
4839 buf.Append ("out ");
4843 protected override string GetTypeDeclaration (TypeDefinition type)
4845 string visibility = GetTypeVisibility (type.Attributes);
4846 if (visibility == null)
4849 StringBuilder buf = new StringBuilder ();
4851 buf.Append (visibility);
4854 MemberFormatter full = new CSharpFullMemberFormatter ();
4856 if (DocUtils.IsDelegate (type)) {
4857 buf.Append("delegate ");
4858 MethodDefinition invoke = type.GetMethod ("Invoke");
4859 buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
4860 buf.Append (GetName (type));
4861 AppendParameters (buf, invoke, invoke.Parameters);
4862 AppendGenericTypeConstraints (buf, type);
4865 return buf.ToString();
4868 if (type.IsAbstract && !type.IsInterface)
4869 buf.Append("abstract ");
4870 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
4871 buf.Append("sealed ");
4872 buf.Replace ("abstract sealed", "static");
4874 buf.Append (GetTypeKind (type));
4876 buf.Append (GetCSharpType (type.FullName) == null
4881 TypeReference basetype = type.BaseType;
4882 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
4885 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
4886 .Select (iface => full.GetName (iface))
4890 if (basetype != null || interface_names.Count > 0)
4893 if (basetype != null) {
4894 buf.Append (full.GetName (basetype));
4895 if (interface_names.Count > 0)
4899 for (int i = 0; i < interface_names.Count; i++){
4902 buf.Append (interface_names [i]);
4904 AppendGenericTypeConstraints (buf, type);
4907 return buf.ToString ();
4910 static string GetTypeKind (TypeDefinition t)
4916 if (t.IsClass || t.FullName == "System.Enum")
4920 throw new ArgumentException(t.FullName);
4923 static string GetTypeVisibility (TypeAttributes ta)
4925 switch (ta & TypeAttributes.VisibilityMask) {
4926 case TypeAttributes.Public:
4927 case TypeAttributes.NestedPublic:
4930 case TypeAttributes.NestedFamily:
4931 case TypeAttributes.NestedFamORAssem:
4939 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4941 if (type.GenericParameters.Count == 0)
4943 return AppendConstraints (buf, type.GenericParameters);
4946 private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
4948 foreach (GenericParameter genArg in genArgs) {
4949 GenericParameterAttributes attrs = genArg.Attributes;
4950 IList<TypeReference> constraints = genArg.Constraints;
4951 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
4954 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
4955 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
4956 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
4959 if (!isref && !isvt && !isnew && constraints.Count == 0)
4961 buf.Append (" where ").Append (genArg.Name).Append (" : ");
4963 buf.Append ("class");
4967 buf.Append ("struct");
4970 if (constraints.Count > 0 && !isvt) {
4973 buf.Append (GetTypeName (constraints [0]));
4974 for (int i = 1; i < constraints.Count; ++i)
4975 buf.Append (", ").Append (GetTypeName (constraints [i]));
4977 if (isnew && !isvt) {
4980 buf.Append ("new()");
4986 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4988 StringBuilder buf = new StringBuilder ();
4989 AppendVisibility (buf, constructor);
4990 if (buf.Length == 0)
4994 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
4995 AppendParameters (buf, constructor, constructor.Parameters);
4998 return buf.ToString ();
5001 protected override string GetMethodDeclaration (MethodDefinition method)
5003 string decl = base.GetMethodDeclaration (method);
5009 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
5011 if (DocUtils.IsExplicitlyImplemented (method)) {
5012 TypeReference iface;
5013 MethodReference ifaceMethod;
5014 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5015 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
5017 .Append (ifaceMethod.Name);
5019 return base.AppendMethodName (buf, method);
5022 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
5024 if (method.GenericParameters.Count == 0)
5026 return AppendConstraints (buf, method.GenericParameters);
5029 protected override string RefTypeModifier {
5033 protected override string GetFinalizerName (MethodDefinition method)
5035 return "~" + method.DeclaringType.Name + " ()";
5038 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
5042 if (method.IsPublic)
5043 return buf.Append ("public");
5044 if (method.IsFamily || method.IsFamilyOrAssembly)
5045 return buf.Append ("protected");
5049 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
5051 string modifiers = String.Empty;
5052 if (method.IsStatic) modifiers += " static";
5053 if (method.IsVirtual && !method.IsAbstract) {
5054 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
5055 else modifiers += " override";
5057 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
5058 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
5059 if (method.IsFinal) modifiers += " sealed";
5060 if (modifiers == " virtual sealed") modifiers = "";
5062 return buf.Append (modifiers);
5065 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
5067 if (method.IsGenericMethod ()) {
5068 IList<GenericParameter> args = method.GenericParameters;
5069 if (args.Count > 0) {
5071 buf.Append (args [0].Name);
5072 for (int i = 1; i < args.Count; ++i)
5073 buf.Append (",").Append (args [i].Name);
5080 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
5082 return AppendParameters (buf, method, parameters, '(', ')');
5085 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
5089 if (parameters.Count > 0) {
5090 if (DocUtils.IsExtensionMethod (method))
5091 buf.Append ("this ");
5092 AppendParameter (buf, parameters [0]);
5093 for (int i = 1; i < parameters.Count; ++i) {
5095 AppendParameter (buf, parameters [i]);
5099 return buf.Append (end);
5102 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
5104 if (parameter.ParameterType is ByReferenceType) {
5105 if (parameter.IsOut)
5106 buf.Append ("out ");
5108 buf.Append ("ref ");
5110 buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
5111 buf.Append (parameter.Name);
5112 if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant) {
5113 buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
5118 protected override string GetPropertyDeclaration (PropertyDefinition property)
5120 MethodDefinition method;
5122 string get_visible = null;
5123 if ((method = property.GetMethod) != null &&
5124 (DocUtils.IsExplicitlyImplemented (method) ||
5125 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5126 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5127 string set_visible = null;
5128 if ((method = property.SetMethod) != null &&
5129 (DocUtils.IsExplicitlyImplemented (method) ||
5130 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5131 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5133 if ((set_visible == null) && (get_visible == null))
5137 StringBuilder buf = new StringBuilder ();
5138 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
5139 buf.Append (visibility = get_visible);
5140 else if (set_visible != null && get_visible == null)
5141 buf.Append (visibility = set_visible);
5143 buf.Append (visibility = "public");
5145 // Pick an accessor to use for static/virtual/override/etc. checks.
5146 method = property.SetMethod;
5148 method = property.GetMethod;
5150 string modifiers = String.Empty;
5151 if (method.IsStatic) modifiers += " static";
5152 if (method.IsVirtual && !method.IsAbstract) {
5153 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
5154 modifiers += " virtual";
5156 modifiers += " override";
5158 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
5159 if (method.IsAbstract && !declDef.IsInterface)
5160 modifiers += " abstract";
5162 modifiers += " sealed";
5163 if (modifiers == " virtual sealed")
5165 buf.Append (modifiers).Append (' ');
5167 buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
5169 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
5170 string name = property.Name;
5171 foreach (MemberReference mi in defs) {
5172 if (mi == property) {
5177 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
5179 if (property.Parameters.Count != 0) {
5180 AppendParameters (buf, method, property.Parameters, '[', ']');
5184 if (get_visible != null) {
5185 if (get_visible != visibility)
5186 buf.Append (' ').Append (get_visible);
5187 buf.Append (" get;");
5189 if (set_visible != null) {
5190 if (set_visible != visibility)
5191 buf.Append (' ').Append (set_visible);
5192 buf.Append (" set;");
5196 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
5199 protected override string GetFieldDeclaration (FieldDefinition field)
5201 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
5202 if (declType.IsEnum && field.Name == "value__")
5203 return null; // This member of enums aren't documented.
5205 StringBuilder buf = new StringBuilder ();
5206 AppendFieldVisibility (buf, field);
5207 if (buf.Length == 0)
5210 if (declType.IsEnum)
5213 if (field.IsStatic && !field.IsLiteral)
5214 buf.Append (" static");
5215 if (field.IsInitOnly)
5216 buf.Append (" readonly");
5217 if (field.IsLiteral)
5218 buf.Append (" const");
5220 buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
5221 buf.Append (field.Name);
5222 AppendFieldValue (buf, field);
5225 return buf.ToString ();
5228 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
5231 return buf.Append ("public");
5232 if (field.IsFamily || field.IsFamilyOrAssembly)
5233 return buf.Append ("protected");
5237 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
5239 // enums have a value__ field, which we ignore
5240 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
5241 field.DeclaringType.IsGenericType ())
5243 if (field.HasConstant && field.IsLiteral) {
5246 val = field.Constant;
5251 buf.Append (" = ").Append ("null");
5252 else if (val is Enum)
5253 buf.Append (" = ").Append (val.ToString ());
5254 else if (val is IFormattable) {
5255 string value = ((IFormattable)val).ToString();
5257 value = "\"" + value + "\"";
5258 buf.Append (" = ").Append (value);
5264 protected override string GetEventDeclaration (EventDefinition e)
5266 StringBuilder buf = new StringBuilder ();
5267 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
5271 AppendModifiers (buf, e.AddMethod);
5273 buf.Append (" event ");
5274 buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters [0]))).Append (' ');
5275 buf.Append (e.Name).Append (';');
5277 return buf.ToString ();
5281 class CSharpMemberFormatter : CSharpFullMemberFormatter {
5282 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5288 class DocTypeFullMemberFormatter : MemberFormatter {
5289 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
5291 protected override char NestedTypeSeparator {
5296 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
5297 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5303 class SlashDocMemberFormatter : MemberFormatter {
5305 protected override char[] GenericTypeContainer {
5306 get {return new char[]{'{', '}'};}
5309 private bool AddTypeCount = true;
5311 private TypeReference genDeclType;
5312 private MethodReference genDeclMethod;
5314 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
5316 if (type is GenericParameter) {
5318 if (genDeclType != null) {
5319 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
5320 for (int i = 0; i < genArgs.Count; ++i) {
5321 if (genArgs [i].Name == type.Name) {
5322 buf.Append ('`').Append (i);
5327 if (genDeclMethod != null) {
5328 IList<GenericParameter> genArgs = null;
5329 if (genDeclMethod.IsGenericMethod ()) {
5330 genArgs = genDeclMethod.GenericParameters;
5331 for (int i = 0; i < genArgs.Count; ++i) {
5332 if (genArgs [i].Name == type.Name) {
5333 buf.Append ("``").Append (i);
5339 if (genDeclType == null && genDeclMethod == null) {
5340 // Probably from within an explicitly implemented interface member,
5341 // where CSC uses parameter names instead of indices (why?), e.g.
5342 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
5343 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
5344 buf.Append (type.Name);
5346 if (buf.Length == l) {
5347 throw new Exception (string.Format (
5348 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
5349 type.Name, genDeclType, genDeclMethod));
5353 base.AppendTypeName (buf, type, context);
5355 int numArgs = type.GenericParameters.Count;
5356 if (type.DeclaringType != null)
5357 numArgs -= type.GenericParameters.Count;
5359 buf.Append ('`').Append (numArgs);
5366 protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
5368 buf.Append (ArrayDelimeters [0]);
5369 int rank = array.Rank;
5372 for (int i = 1; i < rank; ++i) {
5376 return buf.Append (ArrayDelimeters [1]);
5379 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5382 base.AppendGenericType (buf, type, context);
5384 AppendType (buf, type, context);
5388 private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5390 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
5391 bool insertNested = false;
5392 int prevParamCount = 0;
5393 foreach (var decl in decls) {
5395 buf.Append (NestedTypeSeparator);
5396 insertNested = true;
5397 base.AppendTypeName (buf, decl, context);
5398 int argCount = DocUtils.GetGenericArgumentCount (decl);
5399 int numArgs = argCount - prevParamCount;
5400 prevParamCount = argCount;
5402 buf.Append ('`').Append (numArgs);
5407 public override string GetDeclaration (MemberReference member)
5409 TypeReference r = member as TypeReference;
5411 return "T:" + GetTypeName (r);
5413 return base.GetDeclaration (member);
5416 protected override string GetConstructorName (MethodReference constructor)
5418 return GetMethodDefinitionName (constructor, "#ctor");
5421 protected override string GetMethodName (MethodReference method)
5424 MethodDefinition methodDef = method as MethodDefinition;
5425 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
5428 TypeReference iface;
5429 MethodReference ifaceMethod;
5430 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
5431 AddTypeCount = false;
5432 name = GetTypeName (iface) + "." + ifaceMethod.Name;
5433 AddTypeCount = true;
5435 return GetMethodDefinitionName (method, name);
5438 private string GetMethodDefinitionName (MethodReference method, string name)
5440 StringBuilder buf = new StringBuilder ();
5441 buf.Append (GetTypeName (method.DeclaringType));
5443 buf.Append (name.Replace (".", "#"));
5444 if (method.IsGenericMethod ()) {
5445 IList<GenericParameter> genArgs = method.GenericParameters;
5446 if (genArgs.Count > 0)
5447 buf.Append ("``").Append (genArgs.Count);
5449 IList<ParameterDefinition> parameters = method.Parameters;
5451 genDeclType = method.DeclaringType;
5452 genDeclMethod = method;
5453 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
5457 genDeclMethod = null;
5459 return buf.ToString ();
5462 private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
5464 if (parameters.Count == 0)
5469 AppendParameter (buf, genArgs, parameters [0]);
5470 for (int i = 1; i < parameters.Count; ++i) {
5472 AppendParameter (buf, genArgs, parameters [i]);
5475 return buf.Append (')');
5478 private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
5480 AddTypeCount = false;
5481 buf.Append (GetTypeName (parameter.ParameterType));
5482 AddTypeCount = true;
5486 protected override string GetPropertyName (PropertyReference property)
5490 PropertyDefinition propertyDef = property as PropertyDefinition;
5491 MethodDefinition method = null;
5492 if (propertyDef != null)
5493 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
5494 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
5495 name = property.Name;
5497 TypeReference iface;
5498 MethodReference ifaceMethod;
5499 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5500 AddTypeCount = false;
5501 name = string.Join ("#", new string[]{
5502 GetTypeName (iface).Replace (".", "#"),
5503 DocUtils.GetMember (property.Name)
5505 AddTypeCount = true;
5508 StringBuilder buf = new StringBuilder ();
5509 buf.Append (GetName (property.DeclaringType));
5512 IList<ParameterDefinition> parameters = property.Parameters;
5513 if (parameters.Count > 0) {
5514 genDeclType = property.DeclaringType;
5516 IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
5517 AppendParameter (buf, genArgs, parameters [0]);
5518 for (int i = 1; i < parameters.Count; ++i) {
5520 AppendParameter (buf, genArgs, parameters [i]);
5525 return buf.ToString ();
5528 protected override string GetFieldName (FieldReference field)
5530 return string.Format ("{0}.{1}",
5531 GetName (field.DeclaringType), field.Name);
5534 protected override string GetEventName (EventReference e)
5536 return string.Format ("{0}.{1}",
5537 GetName (e.DeclaringType), e.Name);
5540 protected override string GetTypeDeclaration (TypeDefinition type)
5542 string name = GetName (type);
5548 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5550 string name = GetName (constructor);
5556 protected override string GetMethodDeclaration (MethodDefinition method)
5558 string name = GetName (method);
5561 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
5562 genDeclType = method.DeclaringType;
5563 genDeclMethod = method;
5564 name += "~" + GetName (method.ReturnType);
5566 genDeclMethod = null;
5571 protected override string GetPropertyDeclaration (PropertyDefinition property)
5573 string name = GetName (property);
5579 protected override string GetFieldDeclaration (FieldDefinition field)
5581 string name = GetName (field);
5587 protected override string GetEventDeclaration (EventDefinition e)
5589 string name = GetName (e);
5596 class FileNameMemberFormatter : SlashDocMemberFormatter {
5597 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5602 protected override char NestedTypeSeparator {