Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/api-doc-tools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Martinez <joelmartinez@gmail.com>2017-09-28 23:20:38 +0300
committerJoel Martinez <joelmartinez@gmail.com>2017-10-02 23:41:00 +0300
commit8822f7729c04ba22acbe9a9f1141d181ed725b6a (patch)
tree83f3e3c99e6cca9a1080c003e5c273e86eb1be8a /mdoc/Mono.Documentation
parente7b7f22e6a82a7ec02962b8a18700fe4616839df (diff)
mdoc: reformatting the MDocUpdater source code.
We are changing our coding standards, and starting with the update subcommand. This moves many classes into their own code files and namespaces to better organize the source code.
Diffstat (limited to 'mdoc/Mono.Documentation')
-rw-r--r--mdoc/Mono.Documentation/MDocUpdater.cs3284
-rw-r--r--mdoc/Mono.Documentation/Updater/DocUtils.cs273
-rw-r--r--mdoc/Mono.Documentation/Updater/DocsNodeInfo.cs105
-rw-r--r--mdoc/Mono.Documentation/Updater/DocumentationEnumerator.cs378
-rw-r--r--mdoc/Mono.Documentation/Updater/DocumentationImporter.cs8
-rw-r--r--mdoc/Mono.Documentation/Updater/DocumentationMember.cs151
-rw-r--r--mdoc/Mono.Documentation/Updater/DynamicParserContext.cs30
-rw-r--r--mdoc/Mono.Documentation/Updater/EcmaDocumentationEnumerator.cs169
-rw-r--r--mdoc/Mono.Documentation/Updater/EcmaDocumentationImporter.cs118
-rw-r--r--mdoc/Mono.Documentation/Updater/Formatters/ApplePlatformEnumFormatter.cs95
-rw-r--r--mdoc/Mono.Documentation/Updater/Formatters/AttributeValueFormatter.cs66
-rw-r--r--mdoc/Mono.Documentation/Updater/Formatters/CSharpFullMemberFormatter.cs659
-rw-r--r--mdoc/Mono.Documentation/Updater/Formatters/CSharpMemberFormatter.cs14
-rw-r--r--mdoc/Mono.Documentation/Updater/Formatters/CSharpNativeTypeMemberFormatter.cs26
-rw-r--r--mdoc/Mono.Documentation/Updater/Formatters/DefaultAttributeValueFormatter.cs13
-rw-r--r--mdoc/Mono.Documentation/Updater/Formatters/DocIdFormatter.cs23
-rw-r--r--mdoc/Mono.Documentation/Updater/Formatters/DocTypeFullMemberFormatter.cs12
-rw-r--r--mdoc/Mono.Documentation/Updater/Formatters/DocTypeMemberFormatter.cs14
-rw-r--r--mdoc/Mono.Documentation/Updater/Formatters/FileNameMemberFormatter.cs19
-rw-r--r--mdoc/Mono.Documentation/Updater/Formatters/ILFullMemberFormatter.cs573
-rw-r--r--mdoc/Mono.Documentation/Updater/Formatters/ILMemberFormatter.cs14
-rw-r--r--mdoc/Mono.Documentation/Updater/Formatters/ILNativeTypeMemberFormatter.cs10
-rw-r--r--mdoc/Mono.Documentation/Updater/Formatters/MemberFormatter.cs421
-rw-r--r--mdoc/Mono.Documentation/Updater/Formatters/SlashDocMemberFormatter.cs321
-rw-r--r--mdoc/Mono.Documentation/Updater/Formatters/StandardFlagsEnumFormatter.cs34
-rw-r--r--mdoc/Mono.Documentation/Updater/Frameworks/AssemblySet.cs (renamed from mdoc/Mono.Documentation/Frameworks/AssemblySet.cs)2
-rw-r--r--mdoc/Mono.Documentation/Updater/Frameworks/FrameworkEntry.cs (renamed from mdoc/Mono.Documentation/Frameworks/FrameworkEntry.cs)2
-rw-r--r--mdoc/Mono.Documentation/Updater/Frameworks/FrameworkIndex.cs (renamed from mdoc/Mono.Documentation/Frameworks/FrameworkIndex.cs)2
-rw-r--r--mdoc/Mono.Documentation/Updater/Frameworks/FrameworkTypeEntry.cs (renamed from mdoc/Mono.Documentation/Frameworks/FrameworkTypeEntry.cs)2
-rw-r--r--mdoc/Mono.Documentation/Updater/Frameworks/UwpResolver.cs (renamed from mdoc/Mono.Documentation/Frameworks/UwpResolver.cs)2
-rw-r--r--mdoc/Mono.Documentation/Updater/MemberFormatterState.cs8
-rw-r--r--mdoc/Mono.Documentation/Updater/MsxdocDocumentationImporter.cs135
-rw-r--r--mdoc/Mono.Documentation/Updater/ResolvedTypeInfo.cs29
-rw-r--r--mdoc/Mono.Documentation/Util/ApiStyle.cs8
-rw-r--r--mdoc/Mono.Documentation/Util/CecilExtensions.cs120
-rw-r--r--mdoc/Mono.Documentation/Util/NativeTypeManager.cs125
-rw-r--r--mdoc/Mono.Documentation/assembler.cs1
-rw-r--r--mdoc/Mono.Documentation/exceptions.cs3
-rw-r--r--mdoc/Mono.Documentation/monodocer.cs6451
39 files changed, 7263 insertions, 6457 deletions
diff --git a/mdoc/Mono.Documentation/MDocUpdater.cs b/mdoc/Mono.Documentation/MDocUpdater.cs
new file mode 100644
index 00000000..cab63bed
--- /dev/null
+++ b/mdoc/Mono.Documentation/MDocUpdater.cs
@@ -0,0 +1,3284 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Xml;
+using System.Xml.Linq;
+using System.Xml.XPath;
+
+using Mono.Cecil;
+using Mono.Documentation.Updater;
+using Mono.Documentation.Updater.Frameworks;
+using Mono.Documentation.Util;
+using Mono.Options;
+
+using MyXmlNodeList = System.Collections.Generic.List<System.Xml.XmlNode>;
+using StringList = System.Collections.Generic.List<string>;
+using StringToXmlNodeMap = System.Collections.Generic.Dictionary<string, System.Xml.XmlNode>;
+
+namespace Mono.Documentation
+{
+ class MDocUpdater : MDocCommand
+ {
+ string srcPath;
+ List<AssemblySet> assemblies = new List<AssemblySet> ();
+ StringList globalSearchPaths = new StringList ();
+
+ string apistyle = string.Empty;
+ bool isClassicRun;
+
+ bool delete;
+ bool show_exceptions;
+ bool no_assembly_versions, ignore_missing_types;
+ ExceptionLocations? exceptions;
+
+ internal int additions = 0, deletions = 0;
+
+ List<DocumentationImporter> importers = new List<DocumentationImporter> ();
+
+ DocumentationEnumerator docEnum;
+
+ string since;
+
+ static readonly MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
+ static readonly MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
+
+ static MemberFormatter[] typeFormatters = new MemberFormatter[]{
+ new CSharpMemberFormatter (),
+ new ILMemberFormatter (),
+ };
+
+ static MemberFormatter[] memberFormatters = new MemberFormatter[]{
+ new CSharpFullMemberFormatter (),
+ new ILFullMemberFormatter (),
+ };
+
+ internal static readonly MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
+
+ MyXmlNodeList extensionMethods = new MyXmlNodeList ();
+
+ public static string droppedNamespace = string.Empty;
+
+ public static bool HasDroppedNamespace (TypeDefinition forType)
+ {
+ return HasDroppedNamespace (forType.Module);
+ }
+
+ public static bool HasDroppedNamespace (MemberReference forMember)
+ {
+ return HasDroppedNamespace (forMember.Module);
+ }
+
+ public static bool HasDroppedNamespace (AssemblyDefinition forAssembly)
+ {
+ return HasDroppedNamespace (forAssembly.MainModule);
+ }
+
+ public static bool HasDroppedNamespace (ModuleDefinition forModule)
+ {
+ return !string.IsNullOrWhiteSpace (droppedNamespace) && droppedAssemblies.Any (da => da == forModule.Name);
+ }
+
+ public static bool HasDroppedAnyNamespace ()
+ {
+ return !string.IsNullOrWhiteSpace (droppedNamespace);
+ }
+
+ /// <summary>Logic flag to signify that we should list assemblies at the method level, since there are multiple
+ /// assemblies for a given type/method.</summary>
+ public bool IsMultiAssembly
+ {
+ get
+ {
+ return apistyle == "classic" || apistyle == "unified" || !string.IsNullOrWhiteSpace (FrameworksPath);
+ }
+ }
+
+ /// <summary>Path which contains multiple folders with assemblies. Each folder contained will represent one framework.</summary>
+ string FrameworksPath = string.Empty;
+ FrameworkIndex frameworks;
+ FrameworkIndex frameworksCache;
+
+ static List<string> droppedAssemblies = new List<string> ();
+
+ public string PreserveTag { get; set; }
+ public bool DisableSearchDirectoryRecurse = false;
+ public static MDocUpdater Instance { get; private set; }
+ public static bool SwitchingToMagicTypes { get; private set; }
+
+ public override void Run (IEnumerable<string> args)
+ {
+ Instance = this;
+ show_exceptions = DebugOutput;
+ var types = new List<string> ();
+ var p = new OptionSet () {
+ { "delete",
+ "Delete removed members from the XML files.",
+ v => delete = v != null },
+ { "exceptions:",
+ "Document potential exceptions that members can generate. {SOURCES} " +
+ "is a comma-separated list of:\n" +
+ " asm Method calls in same assembly\n" +
+ " depasm Method calls in dependent assemblies\n" +
+ " all Record all possible exceptions\n" +
+ " added Modifier; only create <exception/>s\n" +
+ " for NEW types/members\n" +
+ "If nothing is specified, then only exceptions from the member will " +
+ "be listed.",
+ v => exceptions = ParseExceptionLocations (v) },
+ { "f=",
+ "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
+ v => {
+ switch (v) {
+ case "ignore-missing-types":
+ ignore_missing_types = true;
+ break;
+ case "no-assembly-versions":
+ no_assembly_versions = true;
+ break;
+ default:
+ throw new Exception ("Unsupported flag `" + v + "'.");
+ }
+ } },
+ { "fignore-missing-types",
+ "Do not report an error if a --type=TYPE type\nwas not found.",
+ v => ignore_missing_types = v != null },
+ { "fno-assembly-versions",
+ "Do not generate //AssemblyVersion elements.",
+ v => no_assembly_versions = v != null },
+ { "i|import=",
+ "Import documentation from {FILE}.",
+ v => AddImporter (v) },
+ { "L|lib=",
+ "Check for assembly references in {DIRECTORY}.",
+ v => globalSearchPaths.Add (v) },
+ { "library=",
+ "Ignored for compatibility with update-ecma-xml.",
+ v => {} },
+ { "o|out=",
+ "Root {DIRECTORY} to generate/update documentation.",
+ v => srcPath = v },
+ { "r=",
+ "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
+ "(Equivalent to '-L `dirname ASSEMBLY`'.)",
+ v => globalSearchPaths.Add (Path.GetDirectoryName (v)) },
+ { "since=",
+ "Manually specify the assembly {VERSION} that new members were added in.",
+ v => since = v },
+ { "type=",
+ "Only update documentation for {TYPE}.",
+ v => types.Add (v) },
+ { "dropns=",
+ "When processing assembly {ASSEMBLY}, strip off leading namespace {PREFIX}:\n" +
+ " e.g. --dropns ASSEMBLY=PREFIX",
+ v => {
+ var parts = v.Split ('=');
+ if (parts.Length != 2) { Console.Error.WriteLine ("Invalid dropns input"); return; }
+ var assembly = Path.GetFileName (parts [0].Trim ());
+ var prefix = parts [1].Trim();
+ droppedAssemblies.Add (assembly);
+ droppedNamespace = prefix;
+ } },
+ { "ntypes",
+ "If the new assembly is switching to 'magic types', then this switch should be defined.",
+ v => SwitchingToMagicTypes = true },
+ { "preserve",
+ "Do not delete members that don't exist in the assembly, but rather mark them as preserved.",
+ v => PreserveTag = "true" },
+ { "api-style=",
+ "Denotes the apistyle. Currently, only `classic` and `unified` are supported. `classic` set of assemblies should be run first, immediately followed by 'unified' assemblies with the `dropns` parameter.",
+ v => apistyle = v.ToLowerInvariant () },
+ { "fx|frameworks=",
+ "Configuration XML file, that points to directories which contain libraries that span multiple frameworks.",
+ v => FrameworksPath = v },
+ { "use-docid",
+ "Add 'DocId' to the list of type and member signatures",
+ v =>
+ {
+ typeFormatters = typeFormatters.Union (new MemberFormatter[] { new DocIdFormatter () }).ToArray ();
+ memberFormatters = memberFormatters.Union (new MemberFormatter[] { new DocIdFormatter () }).ToArray ();
+ } },
+ { "disable-searchdir-recurse",
+ "Default behavior for adding search directories ('-L') is to recurse them and search in all subdirectories. This disables that",
+ v => DisableSearchDirectoryRecurse = true },
+ };
+ var assemblyPaths = Parse (p, args, "update",
+ "[OPTIONS]+ ASSEMBLIES",
+ "Create or update documentation from ASSEMBLIES.");
+
+ if (!string.IsNullOrWhiteSpace (FrameworksPath))
+ {
+ var configPath = FrameworksPath;
+ var frameworksDir = FrameworksPath;
+ if (!configPath.EndsWith ("frameworks.xml", StringComparison.InvariantCultureIgnoreCase))
+ configPath = Path.Combine (configPath, "frameworks.xml");
+ else
+ frameworksDir = Path.GetDirectoryName (configPath);
+
+ var fxconfig = XDocument.Load (configPath);
+ var fxd = fxconfig.Root
+ .Elements ("Framework")
+ .Select (f => new
+ {
+ Name = f.Attribute ("Name").Value,
+ Path = Path.Combine (frameworksDir, f.Attribute ("Source").Value),
+ SearchPaths = f.Elements ("assemblySearchPath")
+ .Select (a => Path.Combine (frameworksDir, a.Value))
+ .ToArray (),
+ Imports = f.Elements ("import")
+ .Select (a => Path.Combine (frameworksDir, a.Value))
+ .ToArray ()
+ })
+ .Where (f => Directory.Exists (f.Path));
+
+ Func<string, string, IEnumerable<string>> getFiles = (string path, string filters) =>
+ {
+ return filters
+ .Split ('|')
+ .SelectMany (v => Directory.GetFiles (path, v));
+ };
+
+ var sets = fxd.Select (d => new AssemblySet (
+ d.Name,
+ getFiles (d.Path, "*.dll|*.exe|*.winmd"),
+ this.globalSearchPaths.Union (d.SearchPaths),
+ d.Imports
+ ));
+ this.assemblies.AddRange (sets);
+ assemblyPaths.AddRange (sets.SelectMany (s => s.AssemblyPaths));
+
+ // Create a cache of all frameworks, so we can look up
+ // members that may exist only other frameworks before deleting them
+ Console.Write ("Creating frameworks cache: ");
+ FrameworkIndex cacheIndex = new FrameworkIndex (FrameworksPath);
+ string[] prefixesToAvoid = { "get_", "set_", "add_", "remove_", "raise_" };
+ foreach (var assemblySet in this.assemblies)
+ {
+ using (assemblySet)
+ {
+ Console.Write (".");
+ foreach (var assembly in assemblySet.Assemblies)
+ {
+ var a = cacheIndex.StartProcessingAssembly (assembly, assemblySet.Importers);
+ foreach (var type in assembly.GetTypes ())
+ {
+ var t = a.ProcessType (type);
+ foreach (var member in type.GetMembers ().Where (m => !prefixesToAvoid.Any (pre => m.Name.StartsWith (pre, StringComparison.Ordinal))))
+ t.ProcessMember (member);
+ }
+ }
+ }
+ }
+ Console.WriteLine ($"{Environment.NewLine}done caching.");
+ this.frameworksCache = cacheIndex;
+ }
+ else
+ {
+ this.assemblies.Add (new AssemblySet ("Default", assemblyPaths, this.globalSearchPaths, null));
+ }
+
+ if (assemblyPaths == null)
+ return;
+ if (assemblyPaths.Count == 0)
+ Error ("No assemblies specified.");
+
+ if (!DisableSearchDirectoryRecurse)
+ {
+ // unless it's been explicitly disabled, let's
+ // add all of the subdirectories to the resolver
+ // search paths.
+ foreach (var assemblySet in this.assemblies)
+ assemblySet.RecurseSearchDirectories ();
+ }
+
+ // validation for the api-style parameter
+ if (apistyle == "classic")
+ isClassicRun = true;
+ else if (apistyle == "unified")
+ {
+ if (!droppedAssemblies.Any ())
+ Error ("api-style 'unified' must also supply the 'dropns' parameter with at least one assembly and dropped namespace.");
+ }
+ else if (!string.IsNullOrWhiteSpace (apistyle))
+ Error ("api-style '{0}' is not currently supported", apistyle);
+
+ // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
+
+ if (srcPath == null)
+ throw new InvalidOperationException ("The --out option is required.");
+
+ docEnum = docEnum ?? new DocumentationEnumerator ();
+
+ // PERFORM THE UPDATES
+ frameworks = new FrameworkIndex (FrameworksPath);
+
+ if (types.Count > 0)
+ {
+ types.Sort ();
+ DoUpdateTypes (srcPath, types, srcPath);
+ }
+ else
+ DoUpdateAssemblies (srcPath, srcPath);
+
+ if (!string.IsNullOrWhiteSpace (FrameworksPath))
+ frameworks.WriteToDisk (srcPath);
+
+ Console.WriteLine ("Members Added: {0}, Members Deleted: {1}", additions, deletions);
+ }
+
+ public static bool IsInAssemblies (string name)
+ {
+ return Instance?.assemblies != null ? Instance.assemblies.Any (a => a.Contains (name)) : true;
+ }
+
+ void AddImporter (string path)
+ {
+ var importer = GetImporter (path, supportsEcmaDoc: true);
+ if (importer != null)
+ importers.Add (importer);
+ }
+
+ internal DocumentationImporter GetImporter (string path, bool supportsEcmaDoc)
+ {
+ try
+ {
+ XmlReader r = new XmlTextReader (path);
+ if (r.Read ())
+ {
+ while (r.NodeType != XmlNodeType.Element)
+ {
+ if (!r.Read ())
+ Error ("Unable to read XML file: {0}.", path);
+ }
+ if (r.LocalName == "doc")
+ {
+ return new MsxdocDocumentationImporter (path);
+ }
+ else if (r.LocalName == "Libraries")
+ {
+ if (!supportsEcmaDoc)
+ throw new NotSupportedException ($"Ecma documentation not supported in this mode: {path}");
+
+ var ecmadocs = new XmlTextReader (path);
+ docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
+ return new EcmaDocumentationImporter (ecmadocs);
+ }
+ else
+ Error ("Unsupported XML format within {0}.", path);
+ }
+ r.Close ();
+ }
+ catch (Exception e)
+ {
+ Environment.ExitCode = 1;
+ Error ("Could not load XML file: {0}.", e.Message);
+ }
+ return null;
+ }
+
+ static ExceptionLocations ParseExceptionLocations (string s)
+ {
+ ExceptionLocations loc = ExceptionLocations.Member;
+ if (s == null)
+ return loc;
+ foreach (var type in s.Split (','))
+ {
+ switch (type)
+ {
+ case "added": loc |= ExceptionLocations.AddedMembers; break;
+ case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
+ case "asm": loc |= ExceptionLocations.Assembly; break;
+ case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
+ default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
+ }
+ }
+ return loc;
+ }
+
+ internal void Warning (string format, params object[] args)
+ {
+ Message (TraceLevel.Warning, "mdoc: " + format, args);
+ }
+
+ internal AssemblyDefinition LoadAssembly (string name, IAssemblyResolver assemblyResolver)
+ {
+ AssemblyDefinition assembly = null;
+ try
+ {
+ assembly = AssemblyDefinition.ReadAssembly (name, new ReaderParameters { AssemblyResolver = assemblyResolver });
+ }
+ catch (Exception ex)
+ {
+ Warning ($"Unable to load assembly '{name}': {ex.Message}");
+ }
+
+ return assembly;
+ }
+
+ private static void WriteXml (XmlElement element, System.IO.TextWriter output)
+ {
+ OrderTypeAttributes (element);
+ XmlTextWriter writer = new XmlTextWriter (output);
+ writer.Formatting = Formatting.Indented;
+ writer.Indentation = 2;
+ writer.IndentChar = ' ';
+ element.WriteTo (writer);
+ output.WriteLine ();
+ }
+
+ private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
+ {
+ Action<string> creator = file =>
+ {
+ using (var writer = OpenWrite (file, mode))
+ action (writer);
+ };
+
+ MdocFile.UpdateFile (filename, creator);
+ }
+
+ private static void OrderTypeAttributes (XmlElement e)
+ {
+ foreach (XmlElement type in e.SelectNodes ("//Type"))
+ {
+ OrderTypeAttributes (type.Attributes);
+ }
+ }
+
+ static readonly string[] TypeAttributeOrder = {
+ "Name", "FullName", "FullNameSP", "Maintainer"
+ };
+
+ private static void OrderTypeAttributes (XmlAttributeCollection c)
+ {
+ XmlAttribute[] attrs = new XmlAttribute[TypeAttributeOrder.Length];
+ for (int i = 0; i < c.Count; ++i)
+ {
+ XmlAttribute a = c[i];
+ for (int j = 0; j < TypeAttributeOrder.Length; ++j)
+ {
+ if (a.Name == TypeAttributeOrder[j])
+ {
+ attrs[j] = a;
+ break;
+ }
+ }
+ }
+ for (int i = attrs.Length - 1; i >= 0; --i)
+ {
+ XmlAttribute n = attrs[i];
+ if (n == null)
+ continue;
+ XmlAttribute r = null;
+ for (int j = i + 1; j < attrs.Length; ++j)
+ {
+ if (attrs[j] != null)
+ {
+ r = attrs[j];
+ break;
+ }
+ }
+ if (r == null)
+ continue;
+ if (c[n.Name] != null)
+ {
+ c.RemoveNamedItem (n.Name);
+ c.InsertBefore (n, r);
+ }
+ }
+ }
+
+ private XmlDocument CreateIndexStub ()
+ {
+ XmlDocument index = new XmlDocument ();
+
+ XmlElement index_root = index.CreateElement ("Overview");
+ index.AppendChild (index_root);
+
+ if (assemblies.Count == 0)
+ throw new Exception ("No assembly");
+
+ XmlElement index_assemblies = index.CreateElement ("Assemblies");
+ index_root.AppendChild (index_assemblies);
+
+ XmlElement index_remarks = index.CreateElement ("Remarks");
+ index_remarks.InnerText = "To be added.";
+ index_root.AppendChild (index_remarks);
+
+ XmlElement index_copyright = index.CreateElement ("Copyright");
+ index_copyright.InnerText = "To be added.";
+ index_root.AppendChild (index_copyright);
+
+ XmlElement index_types = index.CreateElement ("Types");
+ index_root.AppendChild (index_types);
+
+ return index;
+ }
+
+ private static void WriteNamespaceStub (string ns, string outdir)
+ {
+ XmlDocument index = new XmlDocument ();
+
+ XmlElement index_root = index.CreateElement ("Namespace");
+ index.AppendChild (index_root);
+
+ index_root.SetAttribute ("Name", ns);
+
+ XmlElement index_docs = index.CreateElement ("Docs");
+ index_root.AppendChild (index_docs);
+
+ XmlElement index_summary = index.CreateElement ("summary");
+ index_summary.InnerText = "To be added.";
+ index_docs.AppendChild (index_summary);
+
+ XmlElement index_remarks = index.CreateElement ("remarks");
+ index_remarks.InnerText = "To be added.";
+ index_docs.AppendChild (index_remarks);
+
+ WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
+ writer => WriteXml (index.DocumentElement, writer));
+ }
+
+ public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
+ {
+ var index = CreateIndexForTypes (dest);
+
+ var found = new HashSet<string> ();
+ foreach (var assemblySet in this.assemblies)
+ {
+ using (assemblySet)
+ {
+ foreach (AssemblyDefinition assembly in assemblySet.Assemblies)
+ {
+ var frameworkEntry = frameworks.StartProcessingAssembly (assembly, assemblySet.Importers);
+
+ foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames))
+ {
+ var typeEntry = frameworkEntry.ProcessType (type);
+
+ string relpath = DoUpdateType (type, typeEntry, basepath, dest);
+ if (relpath == null)
+ continue;
+
+ found.Add (type.FullName);
+
+ if (index == null)
+ continue;
+
+ index.Add (assembly);
+ index.Add (type);
+ }
+ }
+ }
+ }
+
+ if (index != null)
+ index.Write ();
+
+ if (ignore_missing_types)
+ return;
+
+ var notFound = from n in typenames where !found.Contains (n) select n;
+ if (notFound.Any ())
+ throw new InvalidOperationException ("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
+ }
+
+ class IndexForTypes
+ {
+
+ MDocUpdater app;
+ string indexFile;
+
+ XmlDocument index;
+ XmlElement index_types;
+ XmlElement index_assemblies;
+
+ public IndexForTypes (MDocUpdater app, string indexFile, XmlDocument index)
+ {
+ this.app = app;
+ this.indexFile = indexFile;
+ this.index = index;
+
+ index_types = WriteElement (index.DocumentElement, "Types");
+ index_assemblies = WriteElement (index.DocumentElement, "Assemblies");
+ }
+
+ public void Add (AssemblyDefinition assembly)
+ {
+ if (index_assemblies.SelectSingleNode ("Assembly[@Name='" + assembly.Name.Name + "']") != null)
+ return;
+
+ app.AddIndexAssembly (assembly, index_assemblies);
+ }
+
+ public void Add (TypeDefinition type)
+ {
+ app.AddIndexType (type, index_types);
+ }
+
+ public void Write ()
+ {
+ SortIndexEntries (index_types);
+ WriteFile (indexFile, FileMode.Create,
+ writer => WriteXml (index.DocumentElement, writer));
+ }
+ }
+
+ IndexForTypes CreateIndexForTypes (string dest)
+ {
+ string indexFile = Path.Combine (dest, "index.xml");
+ if (File.Exists (indexFile))
+ return null;
+ return new IndexForTypes (this, indexFile, CreateIndexStub ());
+ }
+
+ /// <summary>Constructs the presumed path to the type's documentation file</summary>
+ /// <returns><c>true</c>, if the type file was found, <c>false</c> otherwise.</returns>
+ /// <param name="result">A typle that contains 1) the 'reltypefile', 2) the 'typefile', and 3) the file info</param>
+ bool TryFindTypeFile (string nsname, string typename, string basepath, out Tuple<string, string, FileInfo> result)
+ {
+ string reltypefile = DocUtils.PathCombine (nsname, typename + ".xml");
+ string typefile = Path.Combine (basepath, reltypefile);
+ System.IO.FileInfo file = new System.IO.FileInfo (typefile);
+
+ result = new Tuple<string, string, FileInfo> (reltypefile, typefile, file);
+
+ return file.Exists;
+ }
+
+ public string DoUpdateType (TypeDefinition type, FrameworkTypeEntry typeEntry, string basepath, string dest)
+ {
+ if (type.Namespace == null)
+ Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
+ type.FullName);
+ if (!IsPublic (type))
+ return null;
+
+ // Must get the A+B form of the type name.
+ string typename = GetTypeFileName (type);
+ string nsname = DocUtils.GetNamespace (type);
+
+ // Find the file, if it exists
+ string[] searchLocations = new string[] {
+ nsname
+ };
+
+ if (MDocUpdater.HasDroppedNamespace (type))
+ {
+ // If dropping namespace, types may have moved into a couple of different places.
+ var newSearchLocations = searchLocations.Union (new string[] {
+ string.Format ("{0}.{1}", droppedNamespace, nsname),
+ nsname.Replace (droppedNamespace + ".", string.Empty),
+ MDocUpdater.droppedNamespace
+ });
+
+ searchLocations = newSearchLocations.ToArray ();
+ }
+
+ string reltypefile = "", typefile = "";
+ System.IO.FileInfo file = null;
+
+ foreach (var f in searchLocations)
+ {
+ Tuple<string, string, FileInfo> result;
+ bool fileExists = TryFindTypeFile (f, typename, basepath, out result);
+
+ if (fileExists)
+ {
+ reltypefile = result.Item1;
+ typefile = result.Item2;
+ file = result.Item3;
+
+ break;
+ }
+ }
+
+ if (file == null || !file.Exists)
+ {
+ // we were not able to find a file, let's use the original type informatio.
+ // so that we create the stub in the right place.
+ Tuple<string, string, FileInfo> result;
+ TryFindTypeFile (nsname, typename, basepath, out result);
+
+ reltypefile = result.Item1;
+ typefile = result.Item2;
+ file = result.Item3;
+ }
+
+ string output = null;
+ if (dest == null)
+ {
+ output = typefile;
+ }
+ else if (dest == "-")
+ {
+ output = null;
+ }
+ else
+ {
+ output = Path.Combine (dest, reltypefile);
+ }
+
+ if (file != null && file.Exists)
+ {
+ // Update
+ XmlDocument basefile = new XmlDocument ();
+ try
+ {
+ basefile.Load (typefile);
+ }
+ catch (Exception e)
+ {
+ throw new InvalidOperationException ("Error loading " + typefile + ": " + e.Message, e);
+ }
+
+ DoUpdateType2 ("Updating", basefile, type, typeEntry, output, false);
+ }
+ else
+ {
+ // Stub
+ XmlElement td = StubType (type, output, typeEntry.Framework.Importers);
+ if (td == null)
+ return null;
+ }
+ return reltypefile;
+ }
+
+ private static string GetTypeFileName (TypeReference type)
+ {
+ return filenameFormatter.GetName (type);
+ }
+
+ public static string GetTypeFileName (string typename)
+ {
+ StringBuilder filename = new StringBuilder (typename.Length);
+ int numArgs = 0;
+ int numLt = 0;
+ bool copy = true;
+ for (int i = 0; i < typename.Length; ++i)
+ {
+ char c = typename[i];
+ switch (c)
+ {
+ case '<':
+ copy = false;
+ ++numLt;
+ break;
+ case '>':
+ --numLt;
+ if (numLt == 0)
+ {
+ filename.Append ('`').Append ((numArgs + 1).ToString ());
+ numArgs = 0;
+ copy = true;
+ }
+ break;
+ case ',':
+ if (numLt == 1)
+ ++numArgs;
+ break;
+ default:
+ if (copy)
+ filename.Append (c);
+ break;
+ }
+ }
+ return filename.ToString ();
+ }
+
+ private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
+ {
+ XmlElement index_assembly = null;
+ if (IsMultiAssembly)
+ index_assembly = (XmlElement)parent.SelectSingleNode ("Assembly[@Name='" + assembly.Name.Name + "']");
+
+ if (index_assembly == null)
+ index_assembly = parent.OwnerDocument.CreateElement ("Assembly");
+
+ index_assembly.SetAttribute ("Name", assembly.Name.Name);
+ index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString ());
+
+ AssemblyNameDefinition name = assembly.Name;
+ if (name.HasPublicKey)
+ {
+ XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
+ var key = new StringBuilder (name.PublicKey.Length * 3 + 2);
+ key.Append ("[");
+ foreach (byte b in name.PublicKey)
+ key.AppendFormat ("{0,2:x2} ", b);
+ key.Append ("]");
+ pubkey.InnerText = key.ToString ();
+ index_assembly.AppendChild (pubkey);
+ }
+
+ if (!string.IsNullOrEmpty (name.Culture))
+ {
+ XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
+ culture.InnerText = name.Culture;
+ index_assembly.AppendChild (culture);
+ }
+
+ MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
+ parent.AppendChild (index_assembly);
+ }
+
+ private void AddIndexType (TypeDefinition type, XmlElement index_types)
+ {
+ string typename = GetTypeFileName (type);
+
+ // Add namespace and type nodes into the index file as needed
+ string ns = DocUtils.GetNamespace (type);
+ XmlElement nsnode = (XmlElement)index_types.SelectSingleNode ("Namespace[@Name='" + ns + "']");
+ if (nsnode == null)
+ {
+ nsnode = index_types.OwnerDocument.CreateElement ("Namespace");
+ nsnode.SetAttribute ("Name", ns);
+ index_types.AppendChild (nsnode);
+ }
+ string doc_typename = GetDocTypeName (type);
+ XmlElement typenode = (XmlElement)nsnode.SelectSingleNode ("Type[@Name='" + typename + "']");
+ if (typenode == null)
+ {
+ typenode = index_types.OwnerDocument.CreateElement ("Type");
+ typenode.SetAttribute ("Name", typename);
+ nsnode.AppendChild (typenode);
+ }
+ if (typename != doc_typename)
+ typenode.SetAttribute ("DisplayName", doc_typename);
+ else
+ typenode.RemoveAttribute ("DisplayName");
+
+ typenode.SetAttribute ("Kind", GetTypeKind (type));
+ }
+
+ private void DoUpdateAssemblies (string source, string dest)
+ {
+ string indexfile = dest + "/index.xml";
+ XmlDocument index;
+ if (System.IO.File.Exists (indexfile))
+ {
+ index = new XmlDocument ();
+ index.Load (indexfile);
+
+ // Format change
+ ClearElement (index.DocumentElement, "Assembly");
+ ClearElement (index.DocumentElement, "Attributes");
+ }
+ else
+ {
+ index = CreateIndexStub ();
+ }
+
+ XmlElement index_types = WriteElement (index.DocumentElement, "Types");
+ XmlElement index_assemblies = WriteElement (index.DocumentElement, "Assemblies");
+ if (!IsMultiAssembly)
+ index_assemblies.RemoveAll ();
+
+
+ HashSet<string> goodfiles = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
+
+ int processedAssemblyCount = 0;
+ foreach (var assemblySet in assemblies)
+ {
+ using (assemblySet)
+ {
+ foreach (AssemblyDefinition assm in assemblySet.Assemblies)
+ {
+ AddIndexAssembly (assm, index_assemblies);
+ DoUpdateAssembly (assemblySet, assm, index_types, source, dest, goodfiles);
+ processedAssemblyCount++;
+ }
+ }
+ }
+
+ string defaultTitle = "Untitled";
+ if (processedAssemblyCount == 1)
+ defaultTitle = assemblies[0].Assemblies.First ().Name.Name;
+ WriteElementInitialText (index.DocumentElement, "Title", defaultTitle);
+
+ SortIndexEntries (index_types);
+
+ CleanupFiles (dest, goodfiles);
+ CleanupIndexTypes (index_types, goodfiles);
+ CleanupExtensions (index_types);
+
+ WriteFile (indexfile, FileMode.Create,
+ writer => WriteXml (index.DocumentElement, writer));
+ }
+
+ private static char[] InvalidFilenameChars = { '\\', '/', ':', '*', '?', '"', '<', '>', '|' };
+
+ private void DoUpdateAssembly (AssemblySet assemblySet, AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
+ {
+ var frameworkEntry = frameworks.StartProcessingAssembly (assembly, assemblySet.Importers);
+
+ foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null))
+ {
+ string typename = GetTypeFileName (type);
+ if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0)
+ continue;
+
+ var typeEntry = frameworkEntry.ProcessType (type);
+
+ string reltypepath = DoUpdateType (type, typeEntry, source, dest);
+ if (reltypepath == null)
+ continue;
+
+ // Add namespace and type nodes into the index file as needed
+ AddIndexType (type, index_types);
+
+ // Ensure the namespace index file exists
+ string namespaceToUse = type.Namespace;
+ if (HasDroppedNamespace (assembly))
+ {
+ namespaceToUse = string.Format ("{0}.{1}", droppedNamespace, namespaceToUse);
+ }
+ string onsdoc = DocUtils.PathCombine (dest, namespaceToUse + ".xml");
+ string nsdoc = DocUtils.PathCombine (dest, "ns-" + namespaceToUse + ".xml");
+ if (File.Exists (onsdoc))
+ {
+ File.Move (onsdoc, nsdoc);
+ }
+
+ if (!File.Exists (nsdoc))
+ {
+ Console.WriteLine ("New Namespace File: " + type.Namespace);
+ WriteNamespaceStub (namespaceToUse, dest);
+ }
+
+ goodfiles.Add (reltypepath);
+ }
+ }
+
+ private static void SortIndexEntries (XmlElement indexTypes)
+ {
+ XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
+ XmlNodeComparer c = new AttributeNameComparer ();
+ SortXmlNodes (indexTypes, namespaces, c);
+
+ for (int i = 0; i < namespaces.Count; ++i)
+ SortXmlNodes (namespaces[i], namespaces[i].SelectNodes ("Type"), c);
+ }
+
+ private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
+ {
+ MyXmlNodeList l = new MyXmlNodeList (children.Count);
+ for (int i = 0; i < children.Count; ++i)
+ l.Add (children[i]);
+ l.Sort (comparer);
+ for (int i = l.Count - 1; i > 0; --i)
+ {
+ parent.InsertBefore (parent.RemoveChild ((XmlNode)l[i - 1]), (XmlNode)l[i]);
+ }
+ }
+
+ abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
+ {
+ public abstract int Compare (XmlNode x, XmlNode y);
+
+ public int Compare (object x, object y)
+ {
+ return Compare ((XmlNode)x, (XmlNode)y);
+ }
+ }
+
+ class AttributeNameComparer : XmlNodeComparer
+ {
+ string attribute;
+
+ public AttributeNameComparer ()
+ : this ("Name")
+ {
+ }
+
+ public AttributeNameComparer (string attribute)
+ {
+ this.attribute = attribute;
+ }
+
+ public override int Compare (XmlNode x, XmlNode y)
+ {
+ return x.Attributes[attribute].Value.CompareTo (y.Attributes[attribute].Value);
+ }
+ }
+
+ class VersionComparer : XmlNodeComparer
+ {
+ public override int Compare (XmlNode x, XmlNode y)
+ {
+ // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
+ string a = GetVersion (x.InnerText);
+ string b = GetVersion (y.InnerText);
+ return new Version (a).CompareTo (new Version (b));
+ }
+
+ static string GetVersion (string v)
+ {
+ int n = v.IndexOf ("x");
+ if (n < 0)
+ return v;
+ return v.Substring (0, n - 1);
+ }
+ }
+
+ private static string GetTypeKind (TypeDefinition type)
+ {
+ if (type.IsEnum)
+ return "Enumeration";
+ if (type.IsValueType)
+ return "Structure";
+ if (type.IsInterface)
+ return "Interface";
+ if (DocUtils.IsDelegate (type))
+ return "Delegate";
+ if (type.IsClass || type.FullName == "System.Enum") // FIXME
+ return "Class";
+ throw new ArgumentException ("Unknown kind for type: " + type.FullName);
+ }
+
+ public static bool IsPublic (TypeDefinition type)
+ {
+ TypeDefinition decl = type;
+ while (decl != null)
+ {
+ if (!(decl.IsPublic || decl.IsNestedPublic ||
+ decl.IsNestedFamily || decl.IsNestedFamily || decl.IsNestedFamilyOrAssembly))
+ {
+ return false;
+ }
+ decl = (TypeDefinition)decl.DeclaringType;
+ }
+ return true;
+ }
+
+ private void CleanupFiles (string dest, HashSet<string> goodfiles)
+ {
+ // Look for files that no longer correspond to types
+ foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo (dest).GetDirectories ("*").Where (d => Path.GetFileName (d.FullName) != "FrameworksIndex"))
+ {
+ foreach (System.IO.FileInfo typefile in nsdir.GetFiles ("*.xml"))
+ {
+ string relTypeFile = Path.Combine (nsdir.Name, typefile.Name);
+ if (!goodfiles.Contains (relTypeFile))
+ {
+ XmlDocument doc = new XmlDocument ();
+ doc.Load (typefile.FullName);
+ XmlElement e = doc.SelectSingleNode ("/Type") as XmlElement;
+ var assemblyNameNode = doc.SelectSingleNode ("/Type/AssemblyInfo/AssemblyName");
+ if (assemblyNameNode == null)
+ {
+ Warning ("Did not find /Type/AssemblyInfo/AssemblyName on {0}", typefile.FullName);
+ continue;
+ }
+ string assemblyName = assemblyNameNode.InnerText;
+
+
+ Action saveDoc = () =>
+ {
+ using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
+ WriteXml (doc.DocumentElement, writer);
+ };
+
+ if (!IsMultiAssembly)
+ { // only do this for "regular" runs
+ AssemblyDefinition assembly = assemblies
+ .SelectMany (aset => aset.Assemblies)
+ .FirstOrDefault (a => a.Name.Name == assemblyName);
+ if (e != null && !no_assembly_versions && assembly != null && assemblyName != null && UpdateAssemblyVersions (e, assembly, GetAssemblyVersions (assemblyName), false))
+ {
+ saveDoc ();
+ goodfiles.Add (relTypeFile);
+ continue;
+ }
+ }
+
+ Action actuallyDelete = () =>
+ {
+ string newname = typefile.FullName + ".remove";
+ try { System.IO.File.Delete (newname); } catch (Exception) { Warning ("Unable to delete existing file: {0}", newname); }
+ try { typefile.MoveTo (newname); } catch (Exception) { Warning ("Unable to rename to: {0}", newname); }
+ Console.WriteLine ("Class no longer present; file renamed: " + Path.Combine (nsdir.Name, typefile.Name));
+ };
+
+ if (string.IsNullOrWhiteSpace (PreserveTag))
+ { // only do this if there was not a -preserve
+ saveDoc ();
+
+ var unifiedAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[@apistyle='unified']");
+ var classicAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[not(@apistyle) or @apistyle='classic']");
+ var unifiedMembers = doc.SelectNodes ("//Member[@apistyle='unified']|//Member/AssemblyInfo[@apistyle='unified']");
+ var classicMembers = doc.SelectNodes ("//Member[@apistyle='classic']|//Member/AssemblyInfo[@apistyle='classic']");
+ bool isUnifiedRun = HasDroppedAnyNamespace ();
+ bool isClassicOrNormalRun = !isUnifiedRun;
+
+ Action<XmlNode, ApiStyle> removeStyles = (x, style) =>
+ {
+ var styledNodes = doc.SelectNodes ("//*[@apistyle='" + style.ToString ().ToLowerInvariant () + "']");
+ if (styledNodes != null && styledNodes.Count > 0)
+ {
+ foreach (var node in styledNodes.Cast<XmlNode> ())
+ {
+ node.ParentNode.RemoveChild (node);
+ }
+ }
+ saveDoc ();
+ };
+ if (isClassicOrNormalRun)
+ {
+ if (unifiedAssemblyNode != null || unifiedMembers.Count > 0)
+ {
+ Warning ("*** this type is marked as unified, not deleting during this run: {0}", typefile.FullName);
+ // if truly removed from both assemblies, it will be removed fully during the unified run
+ removeStyles (doc, ApiStyle.Classic);
+ continue;
+ }
+ else
+ {
+ // we should be safe to delete here because it was not marked as a unified assembly
+ actuallyDelete ();
+ }
+ }
+ if (isUnifiedRun)
+ {
+ if (classicAssemblyNode != null || classicMembers.Count > 0)
+ {
+ Warning ("*** this type is marked as classic, not deleting {0}", typefile.FullName);
+ continue;
+ }
+ else
+ {
+ // safe to delete because it wasn't marked as a classic assembly, so the type is gone in both.
+ actuallyDelete ();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static TextWriter OpenWrite (string path, FileMode mode)
+ {
+ var w = new StreamWriter (
+ new FileStream (path, mode),
+ new UTF8Encoding (false)
+ );
+ w.NewLine = "\n";
+ return w;
+ }
+
+ private string[] GetAssemblyVersions (string assemblyName)
+ {
+ return (from a in assemblies.SelectMany (aset => aset.Assemblies)
+ where a.Name.Name == assemblyName
+ select GetAssemblyVersion (a)).ToArray ();
+ }
+
+ private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
+ {
+ // Look for type nodes that no longer correspond to types
+ MyXmlNodeList remove = new MyXmlNodeList ();
+ foreach (XmlElement typenode in index_types.SelectNodes ("Namespace/Type"))
+ {
+ string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute ("Name"), typenode.GetAttribute ("Name") + ".xml");
+ if (!goodfiles.Contains (fulltypename))
+ {
+ remove.Add (typenode);
+ }
+ }
+ foreach (XmlNode n in remove)
+ n.ParentNode.RemoveChild (n);
+ }
+
+ private void CleanupExtensions (XmlElement index_types)
+ {
+ XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
+ if (extensionMethods.Count == 0)
+ {
+ if (e == null)
+ return;
+ index_types.SelectSingleNode ("/Overview").RemoveChild (e);
+ return;
+ }
+ if (e == null)
+ {
+ e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
+ index_types.SelectSingleNode ("/Overview").AppendChild (e);
+ }
+ else
+ e.RemoveAll ();
+ extensionMethods.Sort (DefaultExtensionMethodComparer);
+ foreach (XmlNode m in extensionMethods)
+ {
+ e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
+ }
+ }
+
+ class ExtensionMethodComparer : XmlNodeComparer
+ {
+ public override int Compare (XmlNode x, XmlNode y)
+ {
+ XmlNode xLink = x.SelectSingleNode ("Member/Link");
+ XmlNode yLink = y.SelectSingleNode ("Member/Link");
+
+ int n = xLink.Attributes["Type"].Value.CompareTo (
+ yLink.Attributes["Type"].Value);
+ if (n != 0)
+ return n;
+ n = xLink.Attributes["Member"].Value.CompareTo (
+ yLink.Attributes["Member"].Value);
+ if (n == 0 && !object.ReferenceEquals (x, y))
+ throw new InvalidOperationException ("Duplicate extension method found!");
+ return n;
+ }
+ }
+
+ static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
+
+ public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, FrameworkTypeEntry typeEntry, string output, bool insertSince)
+ {
+ Console.WriteLine (message + ": " + type.FullName);
+
+ StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
+
+ // Update type metadata
+ UpdateType (basefile.DocumentElement, type, typeEntry);
+
+ // Update existing members. Delete member nodes that no longer should be there,
+ // and remember what members are already documented so we don't add them again.
+
+ MyXmlNodeList todelete = new MyXmlNodeList ();
+
+ foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type))
+ {
+ XmlElement oldmember = info.Node;
+ MemberReference oldmember2 = info.Member;
+
+ if (info.Member != null && info.Node != null)
+ {
+ // Check for an error condition where the xml MemberName doesn't match the matched member
+ var memberName = GetMemberName (info.Member);
+ var memberAttribute = info.Node.Attributes["MemberName"];
+ if (memberAttribute == null || (memberAttribute.Value != memberName && memberAttribute.Value.Split (',').Length != memberName.Split (',').Length))
+ {
+ oldmember.SetAttribute ("MemberName", memberName);
+ }
+ }
+
+ string sig = oldmember2 != null ? memberFormatters[1].GetDeclaration (oldmember2) : null;
+
+ // Interface implementations and overrides are deleted from the docs
+ // unless the overrides option is given.
+ if (oldmember2 != null && sig == null)
+ oldmember2 = null;
+
+ // Deleted (or signature changed)
+ if (oldmember2 == null)
+ {
+ if (!string.IsNullOrWhiteSpace (FrameworksPath))
+ {
+ // verify that this member wasn't seen in another framework and is indeed valid
+ var sigFromXml = oldmember
+ .GetElementsByTagName ("MemberSignature")
+ .Cast<XmlElement> ()
+ .FirstOrDefault (x => x.GetAttribute ("Language").Equals ("ILAsm"));
+
+ if (sigFromXml != null)
+ {
+ var sigvalue = sigFromXml.GetAttribute ("Value");
+ Func<FrameworkEntry, bool> findTypes = fx =>
+ {
+ var tInstance = fx.Types.FirstOrDefault (t => t.Equals (typeEntry));
+ if (tInstance != null)
+ {
+ return tInstance.ContainsCSharpSig (sigvalue);
+ }
+ return false;
+ };
+ if (frameworksCache.Frameworks.Any (findTypes))
+ continue;
+ }
+ }
+
+ if (!no_assembly_versions && UpdateAssemblyVersions (oldmember, type.Module.Assembly, new string[] { GetAssemblyVersion (type.Module.Assembly) }, false))
+ continue;
+
+ DeleteMember ("Member Removed", output, oldmember, todelete, type);
+ continue;
+ }
+
+ // Duplicated
+ if (seenmembers.ContainsKey (sig))
+ {
+ if (object.ReferenceEquals (oldmember, seenmembers[sig]))
+ {
+ // ignore, already seen
+ }
+ else
+ DeleteMember ("Duplicate Member Found", output, oldmember, todelete, type);
+
+ continue;
+ }
+
+ // Update signature information
+ UpdateMember (info, typeEntry);
+
+ // get all apistyles of sig from info.Node
+ var styles = oldmember.GetElementsByTagName ("MemberSignature").Cast<XmlElement> ()
+ .Where (x => x.GetAttribute ("Language") == "ILAsm" && !seenmembers.ContainsKey (x.GetAttribute ("Value")))
+ .Select (x => x.GetAttribute ("Value"));
+
+
+ typeEntry.ProcessMember (info.Member);
+
+ foreach (var stylesig in styles)
+ {
+ seenmembers.Add (stylesig, oldmember);
+ }
+ }
+ foreach (XmlElement oldmember in todelete)
+ oldmember.ParentNode.RemoveChild (oldmember);
+
+
+ if (!DocUtils.IsDelegate (type))
+ {
+ XmlNode members = WriteElement (basefile.DocumentElement, "Members");
+ var typemembers = type.GetMembers ()
+ .Where (m =>
+ {
+ if (m is TypeDefinition) return false;
+ string cssig = memberFormatters[0].GetDeclaration (m);
+ if (cssig == null) return false;
+ string sig = memberFormatters[1].GetDeclaration (m);
+ if (seenmembers.ContainsKey (sig)) return false;
+
+ // Verify that the member isn't an explicitly implemented
+ // member of an internal interface, in which case we shouldn't return true.
+ MethodDefinition methdef = null;
+ if (m is MethodDefinition)
+ methdef = m as MethodDefinition;
+ else if (m is PropertyDefinition)
+ {
+ var prop = m as PropertyDefinition;
+ methdef = prop.GetMethod ?? prop.SetMethod;
+ }
+
+ if (methdef != null)
+ {
+ TypeReference iface;
+ MethodReference imethod;
+
+ if (methdef.Overrides.Count == 1 && !methdef.IsPublic)
+ {
+ DocUtils.GetInfoForExplicitlyImplementedMethod (methdef, out iface, out imethod);
+ if (!IsPublic (iface.Resolve ())) return false;
+ }
+ }
+
+ return true;
+ })
+ .ToArray ();
+ foreach (MemberReference m in typemembers)
+ {
+ XmlElement mm = MakeMember (basefile, new DocsNodeInfo (null, m), members, typeEntry);
+ if (mm == null) continue;
+
+ if (MDocUpdater.SwitchingToMagicTypes || MDocUpdater.HasDroppedNamespace (m))
+ {
+ // this is a unified style API that obviously doesn't exist in the classic API. Let's mark
+ // it with apistyle="unified", so that it's not displayed for classic style APIs
+ mm.AddApiStyle (ApiStyle.Unified);
+ }
+
+ Console.WriteLine ("Member Added: " + mm.SelectSingleNode ("MemberSignature/@Value").InnerText);
+ additions++;
+ }
+ }
+
+ // Import code snippets from files
+ foreach (XmlNode code in basefile.GetElementsByTagName ("code"))
+ {
+ if (!(code is XmlElement)) continue;
+ string file = ((XmlElement)code).GetAttribute ("src");
+ string lang = ((XmlElement)code).GetAttribute ("lang");
+ if (file != "")
+ {
+ string src = GetCodeSource (lang, Path.Combine (srcPath, file));
+ if (src != null)
+ code.InnerText = src;
+ }
+ }
+
+ if (insertSince && since != null)
+ {
+ XmlNode docs = basefile.DocumentElement.SelectSingleNode ("Docs");
+ docs.AppendChild (CreateSinceNode (basefile));
+ }
+
+ do
+ {
+ XmlElement d = basefile.DocumentElement["Docs"];
+ XmlElement m = basefile.DocumentElement["Members"];
+ if (d != null && m != null)
+ basefile.DocumentElement.InsertBefore (
+ basefile.DocumentElement.RemoveChild (d), m);
+ SortTypeMembers (m);
+ } while (false);
+
+ if (output == null)
+ WriteXml (basefile.DocumentElement, Console.Out);
+ else
+ {
+ FileInfo file = new FileInfo (output);
+ if (!file.Directory.Exists)
+ {
+ Console.WriteLine ("Namespace Directory Created: " + type.Namespace);
+ file.Directory.Create ();
+ }
+ WriteFile (output, FileMode.Create,
+ writer => WriteXml (basefile.DocumentElement, writer));
+ }
+ }
+
+ private string GetCodeSource (string lang, string file)
+ {
+ int anchorStart;
+ if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0)
+ {
+ // Grab the specified region
+ string region = "#region " + file.Substring (anchorStart + 4);
+ file = file.Substring (0, anchorStart + 3);
+ try
+ {
+ using (StreamReader reader = new StreamReader (file))
+ {
+ string line;
+ StringBuilder src = new StringBuilder ();
+ int indent = -1;
+ while ((line = reader.ReadLine ()) != null)
+ {
+ if (line.Trim () == region)
+ {
+ indent = line.IndexOf (region);
+ continue;
+ }
+ if (indent >= 0 && line.Trim ().StartsWith ("#endregion"))
+ {
+ break;
+ }
+ if (indent >= 0)
+ src.Append (
+ (line.Length > 0 ? line.Substring (indent) : string.Empty) +
+ "\n");
+ }
+ return src.ToString ();
+ }
+ }
+ catch (Exception e)
+ {
+ Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
+ file, region, show_exceptions ? e.ToString () : e.Message);
+ return null;
+ }
+ }
+ try
+ {
+ using (StreamReader reader = new StreamReader (file))
+ return reader.ReadToEnd ();
+ }
+ catch (Exception e)
+ {
+ Warning ("Could not load <code/> file '" + file + "': " + e.Message);
+ }
+ return null;
+ }
+
+ void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete, TypeDefinition type)
+ {
+ string format = output != null
+ ? "{0}: File='{1}'; Signature='{4}'"
+ : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
+ string signature = member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value;
+ Warning (format,
+ reason,
+ output,
+ member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
+ member.Attributes["MemberName"].Value,
+ signature);
+
+ // Identify all of the different states that could affect our decision to delete the member
+ bool shouldPreserve = !string.IsNullOrWhiteSpace (PreserveTag);
+ bool hasContent = MemberDocsHaveUserContent (member);
+ bool shouldDelete = !shouldPreserve && (delete || !hasContent);
+
+ bool unifiedRun = HasDroppedNamespace (type);
+
+ var classicAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[not(@apistyle) or @apistyle='classic']");
+ bool nodeIsClassic = classicAssemblyInfo != null || member.HasApiStyle (ApiStyle.Classic);
+ var unifiedAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']");
+ bool nodeIsUnified = unifiedAssemblyInfo != null || member.HasApiStyle (ApiStyle.Unified);
+
+ Action actuallyDelete = () =>
+ {
+ todelete.Add (member);
+ deletions++;
+ };
+
+ if (!shouldDelete)
+ {
+ // explicitly not deleting
+ string message = shouldPreserve ?
+ "Not deleting '{0}' due to --preserve." :
+ "Not deleting '{0}'; must be enabled with the --delete option";
+ Warning (message, signature);
+ }
+ else if (unifiedRun && nodeIsClassic)
+ {
+ // this is a unified run, and the member doesn't exist, but is marked as being in the classic assembly.
+ member.RemoveApiStyle (ApiStyle.Unified);
+ member.AddApiStyle (ApiStyle.Classic);
+ Warning ("Not removing '{0}' since it's still in the classic assembly.", signature);
+ }
+ else if (unifiedRun && !nodeIsClassic)
+ {
+ // unified run, and the node is not classic, which means it doesn't exist anywhere.
+ actuallyDelete ();
+ }
+ else
+ {
+ if (!isClassicRun || (isClassicRun && !nodeIsClassic && !nodeIsUnified))
+ { // regular codepath (ie. not classic/unified)
+ actuallyDelete ();
+ }
+ else
+ { // this is a classic run
+ Warning ("Removing classic from '{0}' ... will be removed in the unified run if not present there.", signature);
+ member.RemoveApiStyle (ApiStyle.Classic);
+ if (classicAssemblyInfo != null)
+ {
+ member.RemoveChild (classicAssemblyInfo);
+ }
+ }
+ }
+ }
+
+ class MemberComparer : XmlNodeComparer
+ {
+ public override int Compare (XmlNode x, XmlNode y)
+ {
+ int r;
+ string xMemberName = x.Attributes["MemberName"].Value;
+ string yMemberName = y.Attributes["MemberName"].Value;
+
+ // generic methods *end* with '>'
+ // it's possible for explicitly implemented generic interfaces to
+ // contain <...> without being a generic method
+ if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
+ (r = xMemberName.CompareTo (yMemberName)) != 0)
+ return r;
+
+ int lt;
+ if ((lt = xMemberName.IndexOf ("<")) >= 0)
+ xMemberName = xMemberName.Substring (0, lt);
+ if ((lt = yMemberName.IndexOf ("<")) >= 0)
+ yMemberName = yMemberName.Substring (0, lt);
+ if ((r = xMemberName.CompareTo (yMemberName)) != 0)
+ return r;
+
+ // Handle MemberGroup sorting
+ var sc = StringComparison.InvariantCultureIgnoreCase;
+ if (x.Name.Equals ("MemberGroup", sc) || y.Name.Equals ("MemberGroup", sc))
+ {
+ if (x.Name.Equals ("MemberGroup", sc) && y.Name.Equals ("Member", sc))
+ return -1;
+ else if (x.Name.Equals ("Member", sc) && y.Name.Equals ("MemberGroup", sc))
+ return 1;
+ else
+ return xMemberName.CompareTo (yMemberName);
+ }
+
+ // if @MemberName matches, then it's either two different types of
+ // members sharing the same name, e.g. field & property, or it's an
+ // overloaded method.
+ // for different type, sort based on MemberType value.
+ r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
+ y.SelectSingleNode ("MemberType").InnerText);
+ if (r != 0)
+ return r;
+
+ // same type -- must be an overloaded method. Sort based on type
+ // parameter count, then parameter count, then by the parameter
+ // type names.
+ XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
+ XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
+ if (xTypeParams.Count != yTypeParams.Count)
+ return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
+ for (int i = 0; i < xTypeParams.Count; ++i)
+ {
+ r = xTypeParams[i].Attributes["Name"].Value.CompareTo (
+ yTypeParams[i].Attributes["Name"].Value);
+ if (r != 0)
+ return r;
+ }
+
+ XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
+ XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
+ if (xParams.Count != yParams.Count)
+ return xParams.Count <= yParams.Count ? -1 : 1;
+ for (int i = 0; i < xParams.Count; ++i)
+ {
+ r = xParams[i].Attributes["Type"].Value.CompareTo (
+ yParams[i].Attributes["Type"].Value);
+ if (r != 0)
+ return r;
+ }
+ // all parameters match, but return value might not match if it was
+ // changed between one version and another.
+ XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
+ XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
+ if (xReturn != null && yReturn != null)
+ {
+ r = xReturn.InnerText.CompareTo (yReturn.InnerText);
+ if (r != 0)
+ return r;
+ }
+
+ return 0;
+ }
+ }
+
+ static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
+
+ private static void SortTypeMembers (XmlNode members)
+ {
+ if (members == null)
+ return;
+ SortXmlNodes (members, members.SelectNodes ("Member|MemberGroup"), DefaultMemberComparer);
+ }
+
+ private static bool MemberDocsHaveUserContent (XmlNode e)
+ {
+ e = (XmlElement)e.SelectSingleNode ("Docs");
+ if (e == null) return false;
+ foreach (XmlElement d in e.SelectNodes ("*"))
+ if (d.InnerText != "" && !d.InnerText.StartsWith ("To be added"))
+ return true;
+ return false;
+ }
+
+ // UPDATE HELPER FUNCTIONS
+
+ // CREATE A STUB DOCUMENTATION FILE
+
+ public XmlElement StubType (TypeDefinition type, string output, IEnumerable<DocumentationImporter> importers)
+ {
+ string typesig = typeFormatters[0].GetDeclaration (type);
+ if (typesig == null) return null; // not publicly visible
+
+ XmlDocument doc = new XmlDocument ();
+ XmlElement root = doc.CreateElement ("Type");
+ doc.AppendChild (root);
+
+ var frameworkEntry = frameworks.StartProcessingAssembly (type.Module.Assembly, importers);
+ var typeEntry = frameworkEntry.ProcessType (type);
+ DoUpdateType2 ("New Type", doc, type, typeEntry, output, true);
+
+ return root;
+ }
+
+ private XmlElement CreateSinceNode (XmlDocument doc)
+ {
+ XmlElement s = doc.CreateElement ("since");
+ s.SetAttribute ("version", since);
+ return s;
+ }
+
+ // STUBBING/UPDATING FUNCTIONS
+
+ public void UpdateType (XmlElement root, TypeDefinition type, FrameworkTypeEntry typeEntry)
+ {
+ root.SetAttribute ("Name", GetDocTypeName (type));
+ root.SetAttribute ("FullName", GetDocTypeFullName (type));
+
+ foreach (MemberFormatter f in typeFormatters)
+ {
+ string element = "TypeSignature[@Language='" + f.Language + "']";
+ string valueToUse = f.GetDeclaration (type);
+
+ AddXmlNode (
+ root.SelectNodes (element).Cast<XmlElement> ().ToArray (),
+ x => x.GetAttribute ("Value") == valueToUse,
+ x => x.SetAttribute ("Value", valueToUse),
+ () =>
+ {
+ var node = WriteElementAttribute (root, element, "Language", f.Language, forceNewElement: true);
+ var newnode = WriteElementAttribute (root, node, "Value", valueToUse);
+ return newnode;
+ },
+ type);
+ }
+
+ AddAssemblyNameToNode (root, type);
+
+ string assemblyInfoNodeFilter = MDocUpdater.HasDroppedNamespace (type) ? "[@apistyle='unified']" : "[not(@apistyle) or @apistyle='classic']";
+ Func<XmlElement, bool> assemblyFilter = x => x.SelectSingleNode ("AssemblyName").InnerText == type.Module.Assembly.Name.Name;
+ foreach (var ass in root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ().Where (assemblyFilter))
+ {
+ WriteElementText (ass, "AssemblyName", type.Module.Assembly.Name.Name);
+ if (!no_assembly_versions)
+ {
+ UpdateAssemblyVersions (ass, type, true);
+ }
+ else
+ {
+ var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
+ foreach (var version in versions)
+ ass.RemoveChild (version);
+ }
+ if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
+ WriteElementText (ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
+ else
+ ClearElement (ass, "AssemblyCulture");
+
+
+ // Why-oh-why do we put assembly attributes in each type file?
+ // Neither monodoc nor monodocs2html use them, so I'm deleting them
+ // since they're outdated in current docs, and a waste of space.
+ //MakeAttributes(ass, type.Assembly, true);
+ XmlNode assattrs = ass.SelectSingleNode ("Attributes");
+ if (assattrs != null)
+ ass.RemoveChild (assattrs);
+
+ NormalizeWhitespace (ass);
+ }
+
+ if (type.IsGenericType ())
+ {
+ MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace (type));
+ }
+ else
+ {
+ ClearElement (root, "TypeParameters");
+ }
+
+ if (type.BaseType != null)
+ {
+ XmlElement basenode = WriteElement (root, "Base");
+
+ string basetypename = GetDocTypeFullName (type.BaseType);
+ if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
+
+ if (string.IsNullOrWhiteSpace (FrameworksPath))
+ WriteElementText (root, "Base/BaseTypeName", basetypename);
+ else
+ {
+ // Check for the possibility of an alternate inheritance chain in different frameworks
+ var typeElements = basenode.GetElementsByTagName ("BaseTypeName");
+
+ if (typeElements.Count == 0) // no existing elements, just add
+ WriteElementText (root, "Base/BaseTypeName", basetypename);
+ else
+ {
+ // There's already a BaseTypeName, see if it matches
+ if (typeElements[0].InnerText != basetypename)
+ {
+ // Add a framework alternate if one doesn't already exist
+ var existing = typeElements.Cast<XmlNode> ().Where (n => n.InnerText == basetypename);
+ if (!existing.Any ())
+ {
+ var newNode = WriteElementText (basenode, "BaseTypeName", basetypename, forceNewElement: true);
+ WriteElementAttribute (basenode, newNode, "FrameworkAlternate", typeEntry.Framework.Name);
+ }
+ }
+ }
+ }
+
+ // Document how this type instantiates the generic parameters of its base type
+ TypeReference origBase = type.BaseType.GetElementType ();
+ if (origBase.IsGenericType ())
+ {
+ ClearElement (basenode, "BaseTypeArguments");
+ GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
+ IList<TypeReference> baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
+ IList<GenericParameter> baseGenParams = origBase.GenericParameters;
+ if (baseGenArgs.Count != baseGenParams.Count)
+ throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
+ for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++)
+ {
+ GenericParameter param = baseGenParams[i];
+ TypeReference value = baseGenArgs[i];
+
+ XmlElement bta = WriteElement (basenode, "BaseTypeArguments");
+ XmlElement arg = bta.OwnerDocument.CreateElement ("BaseTypeArgument");
+ bta.AppendChild (arg);
+ arg.SetAttribute ("TypeParamName", param.Name);
+ arg.InnerText = GetDocTypeFullName (value);
+ }
+ }
+ }
+ else
+ {
+ ClearElement (root, "Base");
+ }
+
+ if (!DocUtils.IsDelegate (type) && !type.IsEnum)
+ {
+ IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
+ List<string> interface_names = userInterfaces
+ .Select (iface => GetDocTypeFullName (iface))
+ .OrderBy (s => s)
+ .ToList ();
+
+ XmlElement interfaces = WriteElement (root, "Interfaces");
+ interfaces.RemoveAll ();
+ foreach (string iname in interface_names)
+ {
+ XmlElement iface = root.OwnerDocument.CreateElement ("Interface");
+ interfaces.AppendChild (iface);
+ WriteElementText (iface, "InterfaceName", iname);
+ }
+ }
+ else
+ {
+ ClearElement (root, "Interfaces");
+ }
+
+ MakeAttributes (root, GetCustomAttributes (type), type);
+
+ if (DocUtils.IsDelegate (type))
+ {
+ MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace (type));
+ var member = type.GetMethod ("Invoke");
+ MakeParameters (root, member, member.Parameters);
+ MakeReturnValue (root, member);
+ }
+
+ DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement (root, "Docs"), type);
+ MakeDocNode (typeInfo, typeEntry.Framework.Importers);
+
+ if (!DocUtils.IsDelegate (type))
+ WriteElement (root, "Members");
+
+ OrderTypeNodes (root, root.ChildNodes);
+ NormalizeWhitespace (root);
+ }
+
+ /// <summary>Adds an AssemblyInfo with AssemblyName node to an XmlElement.</summary>
+ /// <returns>The assembly that was either added, or was already present</returns>
+ XmlElement AddAssemblyNameToNode (XmlElement root, TypeDefinition type)
+ {
+ return AddAssemblyNameToNode (root, type.Module);
+ }
+
+ /// <summary>Adds an AssemblyInfo with AssemblyName node to an XmlElement.</summary>
+ /// <returns>The assembly that was either added, or was already present</returns>
+ XmlElement AddAssemblyNameToNode (XmlElement root, ModuleDefinition module)
+ {
+ Func<XmlElement, bool> assemblyFilter = x =>
+ {
+ var existingName = x.SelectSingleNode ("AssemblyName");
+
+ bool apiStyleMatches = true;
+ string currentApiStyle = x.GetAttribute ("apistyle");
+ if ((HasDroppedNamespace (module) && !string.IsNullOrWhiteSpace (currentApiStyle) && currentApiStyle != "unified") ||
+ (isClassicRun && (string.IsNullOrWhiteSpace (currentApiStyle) || currentApiStyle != "classic")))
+ {
+ apiStyleMatches = false;
+ }
+ return apiStyleMatches && (existingName == null || (existingName != null && existingName.InnerText == module.Assembly.Name.Name));
+ };
+
+ return AddAssemblyXmlNode (
+ root.SelectNodes ("AssemblyInfo").Cast<XmlElement> ().ToArray (),
+ assemblyFilter, x => WriteElementText (x, "AssemblyName", module.Assembly.Name.Name),
+ () =>
+ {
+ XmlElement ass = WriteElement (root, "AssemblyInfo", forceNewElement: true);
+
+ if (MDocUpdater.HasDroppedNamespace (module))
+ ass.AddApiStyle (ApiStyle.Unified);
+ if (isClassicRun)
+ ass.AddApiStyle (ApiStyle.Classic);
+ return ass;
+ }, module);
+ }
+
+ static readonly string[] TypeNodeOrder = {
+ "TypeSignature",
+ "MemberOfLibrary",
+ "AssemblyInfo",
+ "ThreadingSafetyStatement",
+ "ThreadSafetyStatement",
+ "TypeParameters",
+ "Base",
+ "Interfaces",
+ "Attributes",
+ "Parameters",
+ "ReturnValue",
+ "Docs",
+ "Members",
+ "TypeExcluded",
+ };
+
+ static void OrderTypeNodes (XmlNode member, XmlNodeList children)
+ {
+ ReorderNodes (member, children, TypeNodeOrder);
+ }
+
+ internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
+ {
+ List<T> l = new List<T> (list);
+ l.Sort ();
+ return l;
+ }
+
+ private void UpdateMember (DocsNodeInfo info, FrameworkTypeEntry typeEntry)
+ {
+ XmlElement me = (XmlElement)info.Node;
+ MemberReference mi = info.Member;
+ typeEntry.ProcessMember (mi);
+ foreach (MemberFormatter f in memberFormatters)
+ {
+ string element = "MemberSignature[@Language='" + f.Language + "']";
+
+ var valueToUse = f.GetDeclaration (mi);
+
+ AddXmlNode (
+ me.SelectNodes (element).Cast<XmlElement> ().ToArray (),
+ x => x.GetAttribute ("Value") == valueToUse,
+ x => x.SetAttribute ("Value", valueToUse),
+ () =>
+ {
+ var node = WriteElementAttribute (me, element, "Language", f.Language, forceNewElement: true);
+ var newNode = WriteElementAttribute (me, node, "Value", valueToUse);
+ return newNode;
+ },
+ mi);
+ }
+
+ WriteElementText (me, "MemberType", GetMemberType (mi));
+
+ if (!no_assembly_versions)
+ {
+ if (!IsMultiAssembly)
+ UpdateAssemblyVersions (me, mi, true);
+ else
+ {
+ var node = AddAssemblyNameToNode (me, mi.Module);
+
+ UpdateAssemblyVersionForAssemblyInfo (node, me, new[] { GetAssemblyVersion (mi.Module.Assembly) }, add: true);
+ }
+ }
+ else
+ {
+ ClearElement (me, "AssemblyInfo");
+ }
+
+ MakeAttributes (me, GetCustomAttributes (mi), mi.DeclaringType);
+
+ MakeReturnValue (me, mi, MDocUpdater.HasDroppedNamespace (mi));
+ if (mi is MethodReference)
+ {
+ MethodReference mb = (MethodReference)mi;
+ if (mb.IsGenericMethod ())
+ MakeTypeParameters (me, mb.GenericParameters, mi, MDocUpdater.HasDroppedNamespace (mi));
+ }
+ MakeParameters (me, mi, MDocUpdater.HasDroppedNamespace (mi));
+
+ string fieldValue;
+ if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
+ WriteElementText (me, "MemberValue", fieldValue);
+
+ info.Node = WriteElement (me, "Docs");
+ MakeDocNode (info, typeEntry.Framework.Importers);
+ OrderMemberNodes (me, me.ChildNodes);
+ UpdateExtensionMethods (me, info);
+ }
+
+ static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, MemberReference member)
+ {
+ AddXmlNode (relevant, valueMatches, setValue, makeNewNode, member.Module);
+ }
+
+ static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, TypeDefinition type)
+ {
+ AddXmlNode (relevant, valueMatches, setValue, makeNewNode, type.Module);
+ }
+
+ static XmlElement AddAssemblyXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, ModuleDefinition module)
+ {
+ bool isUnified = MDocUpdater.HasDroppedNamespace (module);
+ XmlElement thisAssemblyNode = relevant.FirstOrDefault (valueMatches);
+ if (thisAssemblyNode == null)
+ {
+ thisAssemblyNode = makeNewNode ();
+ }
+ setValue (thisAssemblyNode);
+
+ if (isUnified)
+ {
+ thisAssemblyNode.AddApiStyle (ApiStyle.Unified);
+
+ foreach (var otherNodes in relevant.Where (n => n != thisAssemblyNode && n.DoesNotHaveApiStyle (ApiStyle.Unified)))
+ {
+ otherNodes.AddApiStyle (ApiStyle.Classic);
+ }
+ }
+ return thisAssemblyNode;
+ }
+
+ /// <summary>Adds an xml node, reusing the node if it's available</summary>
+ /// <param name="relevant">The existing set of nodes</param>
+ /// <param name="valueMatches">Checks to see if the node's value matches what you're trying to write.</param>
+ /// <param name="setValue">Sets the node's value</param>
+ /// <param name="makeNewNode">Creates a new node, if valueMatches returns false.</param>
+ static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, ModuleDefinition module)
+ {
+ bool shouldDuplicate = MDocUpdater.HasDroppedNamespace (module);
+ var styleToUse = shouldDuplicate ? ApiStyle.Unified : ApiStyle.Classic;
+ var existing = relevant;
+ bool done = false;
+ bool addedOldApiStyle = false;
+
+ if (shouldDuplicate)
+ {
+ existing = existing.Where (n => n.HasApiStyle (styleToUse)).ToArray ();
+ foreach (var n in relevant.Where (n => n.DoesNotHaveApiStyle (styleToUse)))
+ {
+ if (valueMatches (n))
+ {
+ done = true;
+ }
+ else
+ {
+ n.AddApiStyle (ApiStyle.Classic);
+ addedOldApiStyle = true;
+ }
+ }
+ }
+ if (!done)
+ {
+ if (!existing.Any ())
+ {
+ var newNode = makeNewNode ();
+ if (shouldDuplicate && addedOldApiStyle)
+ {
+ newNode.AddApiStyle (ApiStyle.Unified);
+ }
+ }
+ else
+ {
+ var itemToReuse = existing.First ();
+ setValue (itemToReuse);
+
+ if (shouldDuplicate && addedOldApiStyle)
+ {
+ itemToReuse.AddApiStyle (styleToUse);
+ }
+ }
+ }
+ }
+
+ static readonly string[] MemberNodeOrder = {
+ "MemberSignature",
+ "MemberType",
+ "AssemblyInfo",
+ "Attributes",
+ "ReturnValue",
+ "TypeParameters",
+ "Parameters",
+ "MemberValue",
+ "Docs",
+ "Excluded",
+ "ExcludedLibrary",
+ "Link",
+ };
+
+ static void OrderMemberNodes (XmlNode member, XmlNodeList children)
+ {
+ ReorderNodes (member, children, MemberNodeOrder);
+ }
+
+ static void ReorderNodes (XmlNode node, XmlNodeList children, string[] ordering)
+ {
+ MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
+ for (int i = 0; i < ordering.Length; ++i)
+ {
+ for (int j = 0; j < children.Count; ++j)
+ {
+ XmlNode c = children[j];
+ if (c.Name == ordering[i])
+ {
+ newChildren.Add (c);
+ }
+ }
+ }
+ if (newChildren.Count >= 0)
+ node.PrependChild ((XmlNode)newChildren[0]);
+ for (int i = 1; i < newChildren.Count; ++i)
+ {
+ XmlNode prev = (XmlNode)newChildren[i - 1];
+ XmlNode cur = (XmlNode)newChildren[i];
+ node.RemoveChild (cur);
+ node.InsertAfter (cur, prev);
+ }
+ }
+
+ IEnumerable<string> GetCustomAttributes (MemberReference mi)
+ {
+ IEnumerable<string> attrs = Enumerable.Empty<string> ();
+
+ ICustomAttributeProvider p = mi as ICustomAttributeProvider;
+ if (p != null)
+ attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
+
+ PropertyDefinition pd = mi as PropertyDefinition;
+ if (pd != null)
+ {
+ if (pd.GetMethod != null)
+ attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
+ if (pd.SetMethod != null)
+ attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
+ }
+
+ EventDefinition ed = mi as EventDefinition;
+ if (ed != null)
+ {
+ if (ed.AddMethod != null)
+ attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
+ if (ed.RemoveMethod != null)
+ attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
+ }
+
+ return attrs;
+ }
+
+ IEnumerable<string> GetCustomAttributes (IList<CustomAttribute> attributes, string prefix)
+ {
+ foreach (CustomAttribute attribute in attributes.OrderBy (ca => ca.AttributeType.FullName))
+ {
+
+ TypeDefinition attrType = attribute.AttributeType as TypeDefinition;
+ if (attrType != null && !IsPublic (attrType))
+ continue;
+ if (slashdocFormatter.GetName (attribute.AttributeType) == null)
+ continue;
+
+ if (Array.IndexOf (IgnorableAttributes, attribute.AttributeType.FullName) >= 0)
+ continue;
+
+ StringList fields = new StringList ();
+
+ for (int i = 0; i < attribute.ConstructorArguments.Count; ++i)
+ {
+ CustomAttributeArgument argument = attribute.ConstructorArguments[i];
+ fields.Add (MakeAttributesValueString (
+ argument.Value,
+ argument.Type));
+ }
+ var namedArgs =
+ (from namedArg in attribute.Fields
+ select new { Type = namedArg.Argument.Type, Name = namedArg.Name, Value = namedArg.Argument.Value })
+ .Concat (
+ (from namedArg in attribute.Properties
+ select new { Type = namedArg.Argument.Type, Name = namedArg.Name, Value = namedArg.Argument.Value }))
+ .OrderBy (v => v.Name);
+ foreach (var d in namedArgs)
+ fields.Add (string.Format ("{0}={1}", d.Name,
+ MakeAttributesValueString (d.Value, d.Type)));
+
+ string a2 = String.Join (", ", fields.ToArray ());
+ if (a2 != "") a2 = "(" + a2 + ")";
+
+ string name = attribute.GetDeclaringType ();
+ if (name.EndsWith ("Attribute")) name = name.Substring (0, name.Length - "Attribute".Length);
+ yield return prefix + name + a2;
+ }
+ }
+
+ static readonly string[] ValidExtensionMembers = {
+ "Docs",
+ "MemberSignature",
+ "MemberType",
+ "Parameters",
+ "ReturnValue",
+ "TypeParameters",
+ };
+
+ static readonly string[] ValidExtensionDocMembers = {
+ "param",
+ "summary",
+ "typeparam",
+ };
+
+ private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
+ {
+ MethodDefinition me = info.Member as MethodDefinition;
+ if (me == null)
+ return;
+ if (info.Parameters.Count < 1)
+ return;
+ if (!DocUtils.IsExtensionMethod (me))
+ return;
+
+ XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
+ XmlNode member = e.CloneNode (true);
+ em.AppendChild (member);
+ RemoveExcept (member, ValidExtensionMembers);
+ RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
+ WriteElementText (member, "MemberType", "ExtensionMethod");
+ XmlElement link = member.OwnerDocument.CreateElement ("Link");
+ var linktype = slashdocFormatter.GetName (me.DeclaringType);
+ var linkmember = slashdocFormatter.GetDeclaration (me);
+ link.SetAttribute ("Type", linktype);
+ link.SetAttribute ("Member", linkmember);
+ member.AppendChild (link);
+ AddTargets (em, info);
+
+ if (!IsMultiAssembly || (IsMultiAssembly && !extensionMethods.Any (ex => ex.SelectSingleNode ("Member/Link/@Type").Value == linktype && ex.SelectSingleNode ("Member/Link/@Member").Value == linkmember)))
+ {
+ extensionMethods.Add (em);
+ }
+ }
+
+ private static void RemoveExcept (XmlNode node, string[] except)
+ {
+ if (node == null)
+ return;
+ MyXmlNodeList remove = null;
+ foreach (XmlNode n in node.ChildNodes)
+ {
+ if (Array.BinarySearch (except, n.Name) < 0)
+ {
+ if (remove == null)
+ remove = new MyXmlNodeList ();
+ remove.Add (n);
+ }
+ }
+ if (remove != null)
+ foreach (XmlNode n in remove)
+ node.RemoveChild (n);
+ }
+
+ private static void AddTargets (XmlNode member, DocsNodeInfo info)
+ {
+ XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
+ member.PrependChild (targets);
+ if (!(info.Parameters[0].ParameterType is GenericParameter))
+ {
+ var reference = info.Parameters[0].ParameterType;
+ TypeReference typeReference = reference as TypeReference;
+ var declaration = reference != null ?
+ slashdocFormatter.GetDeclaration (typeReference) :
+ slashdocFormatter.GetDeclaration (reference);
+
+ AppendElementAttributeText (targets, "Target", "Type", declaration);
+ }
+ else
+ {
+ GenericParameter gp = (GenericParameter)info.Parameters[0].ParameterType;
+ IList<TypeReference> constraints = gp.Constraints;
+ if (constraints.Count == 0)
+ AppendElementAttributeText (targets, "Target", "Type", "System.Object");
+ else
+ foreach (TypeReference c in constraints)
+ AppendElementAttributeText (targets, "Target", "Type",
+ slashdocFormatter.GetDeclaration (c));
+ }
+ }
+
+ private static bool GetFieldConstValue (FieldDefinition field, out string value)
+ {
+ value = null;
+ TypeDefinition type = field.DeclaringType.Resolve ();
+ if (type != null && type.IsEnum) return false;
+
+ if (type != null && type.IsGenericType ()) return false;
+ if (!field.HasConstant)
+ return false;
+ if (field.IsLiteral)
+ {
+ object val = field.Constant;
+ if (val == null) value = "null";
+ else if (val is Enum) value = val.ToString ();
+ else if (val is IFormattable)
+ {
+ value = ((IFormattable)val).ToString (null, CultureInfo.InvariantCulture);
+ if (val is string)
+ value = "\"" + value + "\"";
+ }
+ if (value != null && value != "")
+ return true;
+ }
+ return false;
+ }
+
+ // XML HELPER FUNCTIONS
+
+ internal static XmlElement WriteElement (XmlNode parent, string element, bool forceNewElement = false)
+ {
+ XmlElement ret = (XmlElement)parent.SelectSingleNode (element);
+ if (ret == null || forceNewElement)
+ {
+ string[] path = element.Split ('/');
+ foreach (string p in path)
+ {
+ ret = (XmlElement)parent.SelectSingleNode (p);
+ if (ret == null || forceNewElement)
+ {
+ string ename = p;
+ if (ename.IndexOf ('[') >= 0) // strip off XPath predicate
+ ename = ename.Substring (0, ename.IndexOf ('['));
+ ret = parent.OwnerDocument.CreateElement (ename);
+ parent.AppendChild (ret);
+ parent = ret;
+ }
+ else
+ {
+ parent = ret;
+ }
+ }
+ }
+ return ret;
+ }
+ private static XmlElement WriteElementText (XmlNode parent, string element, string value, bool forceNewElement = false)
+ {
+ XmlElement node = WriteElement (parent, element, forceNewElement: forceNewElement);
+ node.InnerText = value;
+ return node;
+ }
+
+ static XmlElement AppendElementText (XmlNode parent, string element, string value)
+ {
+ XmlElement n = parent.OwnerDocument.CreateElement (element);
+ parent.AppendChild (n);
+ n.InnerText = value;
+ return n;
+ }
+
+ static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
+ {
+ XmlElement n = parent.OwnerDocument.CreateElement (element);
+ parent.AppendChild (n);
+ n.SetAttribute (attribute, value);
+ return n;
+ }
+
+ internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
+ {
+ XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
+ dest.AppendChild (copy);
+ return copy;
+ }
+
+ private static void WriteElementInitialText (XmlElement parent, string element, string value)
+ {
+ XmlElement node = (XmlElement)parent.SelectSingleNode (element);
+ if (node != null)
+ return;
+ node = WriteElement (parent, element);
+ node.InnerText = value;
+ }
+ private static XmlElement WriteElementAttribute (XmlElement parent, string element, string attribute, string value, bool forceNewElement = false)
+ {
+ XmlElement node = WriteElement (parent, element, forceNewElement: forceNewElement);
+ return WriteElementAttribute (parent, node, attribute, value);
+ }
+ private static XmlElement WriteElementAttribute (XmlElement parent, XmlElement node, string attribute, string value)
+ {
+ if (node.GetAttribute (attribute) != value)
+ {
+ node.SetAttribute (attribute, value);
+ }
+ return node;
+ }
+ internal static void ClearElement (XmlElement parent, string name)
+ {
+ XmlElement node = (XmlElement)parent.SelectSingleNode (name);
+ if (node != null)
+ parent.RemoveChild (node);
+ }
+
+ // DOCUMENTATION HELPER FUNCTIONS
+
+ private void MakeDocNode (DocsNodeInfo info, IEnumerable<DocumentationImporter> setimporters)
+ {
+ List<GenericParameter> genericParams = info.GenericParameters;
+ IList<ParameterDefinition> parameters = info.Parameters;
+ TypeReference returntype = info.ReturnType;
+ bool returnisreturn = info.ReturnIsReturn;
+ XmlElement e = info.Node;
+ bool addremarks = info.AddRemarks;
+
+ WriteElementInitialText (e, "summary", "To be added.");
+
+ if (parameters != null)
+ {
+ string[] values = new string[parameters.Count];
+ for (int i = 0; i < values.Length; ++i)
+ values[i] = parameters[i].Name;
+ UpdateParameters (e, "param", values);
+ }
+
+ if (genericParams != null)
+ {
+ string[] values = new string[genericParams.Count];
+ for (int i = 0; i < values.Length; ++i)
+ values[i] = genericParams[i].Name;
+ UpdateParameters (e, "typeparam", values);
+ }
+
+ string retnodename = null;
+ if (returntype != null && returntype.FullName != "System.Void")
+ { // FIXME
+ retnodename = returnisreturn ? "returns" : "value";
+ string retnodename_other = !returnisreturn ? "returns" : "value";
+
+ // If it has a returns node instead of a value node, change its name.
+ XmlElement retother = (XmlElement)e.SelectSingleNode (retnodename_other);
+ if (retother != null)
+ {
+ XmlElement retnode = e.OwnerDocument.CreateElement (retnodename);
+ foreach (XmlNode node in retother)
+ retnode.AppendChild (node.CloneNode (true));
+ e.ReplaceChild (retnode, retother);
+ }
+ else
+ {
+ WriteElementInitialText (e, retnodename, "To be added.");
+ }
+ }
+ else
+ {
+ ClearElement (e, "returns");
+ ClearElement (e, "value");
+ }
+
+ if (addremarks)
+ WriteElementInitialText (e, "remarks", "To be added.");
+
+ if (exceptions.HasValue && info.Member != null &&
+ (exceptions.Value & ExceptionLocations.AddedMembers) == 0)
+ {
+ UpdateExceptions (e, info.Member);
+ }
+
+ foreach (DocumentationImporter importer in importers)
+ {
+ importer.ImportDocumentation (info);
+ }
+ if (setimporters != null)
+ {
+ foreach (var i in setimporters)
+ i.ImportDocumentation (info);
+ }
+
+ OrderDocsNodes (e, e.ChildNodes);
+ NormalizeWhitespace (e);
+ }
+
+ static readonly string[] DocsNodeOrder = {
+ "typeparam", "param", "summary", "returns", "value", "remarks",
+ };
+
+ private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
+ {
+ ReorderNodes (docs, children, DocsNodeOrder);
+ }
+
+
+ private void UpdateParameters (XmlElement e, string element, string[] values)
+ {
+ if (values != null)
+ {
+ XmlNode[] paramnodes = new XmlNode[values.Length];
+
+ // Some documentation had param nodes with leading spaces.
+ foreach (XmlElement paramnode in e.SelectNodes (element))
+ {
+ paramnode.SetAttribute ("name", paramnode.GetAttribute ("name").Trim ());
+ }
+
+ // If a member has only one parameter, we can track changes to
+ // the name of the parameter easily.
+ if (values.Length == 1 && e.SelectNodes (element).Count == 1)
+ {
+ UpdateParameterName (e, (XmlElement)e.SelectSingleNode (element), values[0]);
+ }
+
+ bool reinsert = false;
+
+ // Pick out existing and still-valid param nodes, and
+ // create nodes for parameters not in the file.
+ Hashtable seenParams = new Hashtable ();
+ for (int pi = 0; pi < values.Length; pi++)
+ {
+ string p = values[pi];
+ seenParams[p] = pi;
+
+ paramnodes[pi] = e.SelectSingleNode (element + "[@name='" + p + "']");
+ if (paramnodes[pi] != null) continue;
+
+ XmlElement pe = e.OwnerDocument.CreateElement (element);
+ pe.SetAttribute ("name", p);
+ pe.InnerText = "To be added.";
+ paramnodes[pi] = pe;
+ reinsert = true;
+ }
+
+ // Remove parameters that no longer exist and check all params are in the right order.
+ int idx = 0;
+ MyXmlNodeList todelete = new MyXmlNodeList ();
+ foreach (XmlElement paramnode in e.SelectNodes (element))
+ {
+ string name = paramnode.GetAttribute ("name");
+ if (!seenParams.ContainsKey (name))
+ {
+ if (!delete && !paramnode.InnerText.StartsWith ("To be added"))
+ {
+ Warning ("The following param node can only be deleted if the --delete option is given: ");
+ if (e.ParentNode == e.OwnerDocument.DocumentElement)
+ {
+ // delegate type
+ Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
+ e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
+ name);
+ }
+ else
+ {
+ Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
+ e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
+ e.ParentNode.Attributes["MemberName"].Value,
+ name);
+ }
+ Warning ("\tValue={0}", paramnode.OuterXml);
+ }
+ else
+ {
+ todelete.Add (paramnode);
+ }
+ continue;
+ }
+
+ if ((int)seenParams[name] != idx)
+ reinsert = true;
+
+ idx++;
+ }
+
+ foreach (XmlNode n in todelete)
+ {
+ n.ParentNode.RemoveChild (n);
+ }
+
+ // Re-insert the parameter nodes at the top of the doc section.
+ if (reinsert)
+ for (int pi = values.Length - 1; pi >= 0; pi--)
+ e.PrependChild (paramnodes[pi]);
+ }
+ else
+ {
+ // Clear all existing param nodes
+ foreach (XmlNode paramnode in e.SelectNodes (element))
+ {
+ if (!delete && !paramnode.InnerText.StartsWith ("To be added"))
+ {
+ Console.WriteLine ("The following param node can only be deleted if the --delete option is given:");
+ Console.WriteLine (paramnode.OuterXml);
+ }
+ else
+ {
+ paramnode.ParentNode.RemoveChild (paramnode);
+ }
+ }
+ }
+ }
+
+ private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
+ {
+ string existingName = pe.GetAttribute ("name");
+ pe.SetAttribute ("name", newName);
+ if (existingName == newName)
+ return;
+ foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
+ if (paramref.GetAttribute ("name").Trim () == existingName)
+ paramref.SetAttribute ("name", newName);
+ }
+
+ class CrefComparer : XmlNodeComparer
+ {
+
+ public CrefComparer ()
+ {
+ }
+
+ public override int Compare (XmlNode x, XmlNode y)
+ {
+ string xType = x.Attributes["cref"].Value;
+ string yType = y.Attributes["cref"].Value;
+ string xNamespace = GetNamespace (xType);
+ string yNamespace = GetNamespace (yType);
+
+ int c = xNamespace.CompareTo (yNamespace);
+ if (c != 0)
+ return c;
+ return xType.CompareTo (yType);
+ }
+
+ static string GetNamespace (string type)
+ {
+ int n = type.LastIndexOf ('.');
+ if (n >= 0)
+ return type.Substring (0, n);
+ return string.Empty;
+ }
+ }
+
+ private void UpdateExceptions (XmlNode docs, MemberReference member)
+ {
+ string indent = new string (' ', 10);
+ foreach (var source in new ExceptionLookup (exceptions.Value)[member])
+ {
+ string cref = slashdocFormatter.GetDeclaration (source.Exception);
+ var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
+ if (node != null)
+ continue;
+ XmlElement e = docs.OwnerDocument.CreateElement ("exception");
+ e.SetAttribute ("cref", cref);
+ e.InnerXml = "To be added; from:\n" + indent + "<see cref=\"" +
+ string.Join ("\" />,\n" + indent + "<see cref=\"",
+ source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
+ .OrderBy (s => s)) +
+ "\" />";
+ docs.AppendChild (e);
+ }
+ SortXmlNodes (docs, docs.SelectNodes ("exception"),
+ new CrefComparer ());
+ }
+
+ private static void NormalizeWhitespace (XmlElement e)
+ {
+ // Remove all text and whitespace nodes from the element so it
+ // is outputted with nice indentation and no blank lines.
+ ArrayList deleteNodes = new ArrayList ();
+ foreach (XmlNode n in e)
+ if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
+ deleteNodes.Add (n);
+ foreach (XmlNode n in deleteNodes)
+ n.ParentNode.RemoveChild (n);
+ }
+
+ private bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
+ {
+ TypeDefinition type = member as TypeDefinition;
+ if (type == null)
+ type = member.DeclaringType as TypeDefinition;
+
+ var versions = new string[] { GetAssemblyVersion (type.Module.Assembly) };
+
+ if (root.LocalName == "AssemblyInfo")
+ return UpdateAssemblyVersionForAssemblyInfo (root, root.ParentNode as XmlElement, versions, add: true);
+ else
+ return UpdateAssemblyVersions (root, type.Module.Assembly, versions, add);
+ }
+
+ private static string GetAssemblyVersion (AssemblyDefinition assembly)
+ {
+ return assembly.Name.Version.ToString ();
+ }
+
+ private bool UpdateAssemblyVersions (XmlElement root, AssemblyDefinition assembly, string[] assemblyVersions, bool add)
+ {
+ if (IsMultiAssembly)
+ return false;
+
+ XmlElement av = (XmlElement)root.SelectSingleNode ("AssemblyVersions");
+ if (av != null)
+ {
+ // AssemblyVersions is not part of the spec
+ root.RemoveChild (av);
+ }
+
+ string oldNodeFilter = "AssemblyInfo[not(@apistyle) or @apistyle='classic']";
+ string newNodeFilter = "AssemblyInfo[@apistyle='unified']";
+ string thisNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? newNodeFilter : oldNodeFilter;
+ string thatNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? oldNodeFilter : newNodeFilter;
+
+ XmlElement e = (XmlElement)root.SelectSingleNode (thisNodeFilter);
+ if (e == null)
+ {
+ e = root.OwnerDocument.CreateElement ("AssemblyInfo");
+
+ if (MDocUpdater.HasDroppedNamespace (assembly))
+ {
+ e.AddApiStyle (ApiStyle.Unified);
+ }
+
+ root.AppendChild (e);
+ }
+
+ var thatNode = (XmlElement)root.SelectSingleNode (thatNodeFilter);
+ if (MDocUpdater.HasDroppedNamespace (assembly) && thatNode != null)
+ {
+ // there's a classic node, we should add apistyles
+ e.AddApiStyle (ApiStyle.Unified);
+ thatNode.AddApiStyle (ApiStyle.Classic);
+ }
+
+ return UpdateAssemblyVersionForAssemblyInfo (e, root, assemblyVersions, add);
+ }
+
+ static bool UpdateAssemblyVersionForAssemblyInfo (XmlElement e, XmlElement root, string[] assemblyVersions, bool add)
+ {
+ List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().Where (v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0).ToList ();
+ // matches.Count > 0 && add: ignore -- already present
+ if (matches.Count > 0 && !add)
+ {
+ foreach (XmlNode c in matches)
+ e.RemoveChild (c);
+ }
+ else if (matches.Count == 0 && add)
+ {
+ foreach (string sv in assemblyVersions)
+ {
+ XmlElement c = root.OwnerDocument.CreateElement ("AssemblyVersion");
+ c.InnerText = sv;
+ e.AppendChild (c);
+ }
+ }
+
+ // matches.Count == 0 && !add: ignore -- already not present
+ XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
+ SortXmlNodes (e, avs, new VersionComparer ());
+
+ bool anyNodesLeft = avs.Count != 0;
+ if (!anyNodesLeft)
+ {
+ e.ParentNode.RemoveChild (e);
+ }
+ return anyNodesLeft;
+ }
+
+ // FIXME: get TypeReferences instead of string comparison?
+ private static string[] IgnorableAttributes = {
+ // Security related attributes
+ "System.Reflection.AssemblyKeyFileAttribute",
+ "System.Reflection.AssemblyDelaySignAttribute",
+ // Present in @RefType
+ "System.Runtime.InteropServices.OutAttribute",
+ // For naming the indexer to use when not using indexers
+ "System.Reflection.DefaultMemberAttribute",
+ // for decimal constants
+ "System.Runtime.CompilerServices.DecimalConstantAttribute",
+ // compiler generated code
+ "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
+ // more compiler generated code, e.g. iterator methods
+ "System.Diagnostics.DebuggerHiddenAttribute",
+ "System.Runtime.CompilerServices.FixedBufferAttribute",
+ "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
+ // extension methods
+ "System.Runtime.CompilerServices.ExtensionAttribute",
+ // Used to differentiate 'object' from C#4 'dynamic'
+ "System.Runtime.CompilerServices.DynamicAttribute",
+ };
+
+ private IEnumerable<char> FilterSpecialChars (string value)
+ {
+ foreach (char c in value)
+ {
+ switch (c)
+ {
+ case '\0':
+ yield return '\\';
+ yield return '0';
+ break;
+ case '\t':
+ yield return '\\';
+ yield return 't';
+ break;
+ case '\n':
+ yield return '\\';
+ yield return 'n';
+ break;
+ case '\r':
+ yield return '\\';
+ yield return 'r';
+ break;
+ case '\f':
+ yield return '\\';
+ yield return 'f';
+ break;
+ case '\b':
+ yield return '\\';
+ yield return 'b';
+ break;
+ default:
+ yield return c;
+ break;
+ }
+ }
+ }
+
+ private void MakeAttributes (XmlElement root, IEnumerable<string> attributes, TypeReference t = null)
+ {
+ if (!attributes.Any ())
+ {
+ ClearElement (root, "Attributes");
+ return;
+ }
+
+ XmlElement e = (XmlElement)root.SelectSingleNode ("Attributes");
+ if (e != null)
+ e.RemoveAll ();
+ else if (e == null)
+ e = root.OwnerDocument.CreateElement ("Attributes");
+
+
+ foreach (string attribute in attributes)
+ {
+ XmlElement ae = root.OwnerDocument.CreateElement ("Attribute");
+ e.AppendChild (ae);
+ var value = new String (FilterSpecialChars (attribute).ToArray ());
+ WriteElementText (ae, "AttributeName", value);
+ }
+
+ if (e.ParentNode == null)
+ root.AppendChild (e);
+
+ NormalizeWhitespace (e);
+ }
+
+ public static string MakeAttributesValueString (object v, TypeReference valueType)
+ {
+ var formatters = new[] {
+ new AttributeValueFormatter (),
+ new ApplePlatformEnumFormatter (),
+ new StandardFlagsEnumFormatter (),
+ new DefaultAttributeValueFormatter (),
+ };
+
+ ResolvedTypeInfo type = new ResolvedTypeInfo (valueType);
+ foreach (var formatter in formatters)
+ {
+ string formattedValue;
+ if (formatter.TryFormatValue (v, type, out formattedValue))
+ {
+ return formattedValue;
+ }
+ }
+
+ // this should never occur because the DefaultAttributeValueFormatter will always
+ // successfully format the value ... but this is needed to satisfy the compiler :)
+ throw new InvalidDataException (string.Format ("Unable to format attribute value ({0})", v.ToString ()));
+ }
+
+ internal static IDictionary<long, string> GetEnumerationValues (TypeDefinition type)
+ {
+ var values = new Dictionary<long, string> ();
+ foreach (var f in
+ (from f in type.Fields
+ where !(f.IsRuntimeSpecialName || f.IsSpecialName)
+ select f))
+ {
+ values[ToInt64 (f.Constant)] = f.Name;
+ }
+ return values;
+ }
+
+ internal static long ToInt64 (object value)
+ {
+ if (value is ulong)
+ return (long)(ulong)value;
+ return Convert.ToInt64 (value);
+ }
+
+ private void MakeParameters (XmlElement root, MemberReference member, IList<ParameterDefinition> parameters, bool shouldDuplicateWithNew = false)
+ {
+ XmlElement e = WriteElement (root, "Parameters");
+
+ int i = 0;
+ foreach (ParameterDefinition p in parameters)
+ {
+ XmlElement pe;
+
+ // param info
+ var ptype = GetDocParameterType (p.ParameterType);
+ var newPType = ptype;
+
+ if (MDocUpdater.SwitchingToMagicTypes)
+ {
+ newPType = NativeTypeManager.ConvertFromNativeType (ptype);
+ }
+
+ // now find the existing node, if it's there so we can reuse it.
+ var nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
+ .Cast<XmlElement> ().Where (x => x.GetAttribute ("Name") == p.Name)
+ .ToArray ();
+
+ if (nodes.Count () == 0)
+ {
+ // wasn't found, let's make sure it wasn't just cause the param name was changed
+ nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
+ .Cast<XmlElement> ()
+ .Skip (i) // this makes sure we don't inadvertently "reuse" nodes when adding new ones
+ .Where (x => x.GetAttribute ("Name") != p.Name && (x.GetAttribute ("Type") == ptype || x.GetAttribute ("Type") == newPType))
+ .Take (1) // there might be more than one that meets this parameter ... only take the first.
+ .ToArray ();
+ }
+
+ AddXmlNode (nodes,
+ x => x.GetAttribute ("Type") == ptype,
+ x => x.SetAttribute ("Type", ptype),
+ () =>
+ {
+ pe = root.OwnerDocument.CreateElement ("Parameter");
+ e.AppendChild (pe);
+
+ pe.SetAttribute ("Name", p.Name);
+ pe.SetAttribute ("Type", ptype);
+ if (p.ParameterType is ByReferenceType)
+ {
+ if (p.IsOut)
+ pe.SetAttribute ("RefType", "out");
+ else
+ pe.SetAttribute ("RefType", "ref");
+ }
+
+ MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
+ return pe;
+ },
+ member);
+
+ i++;
+ }
+ }
+
+ private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams, MemberReference member, bool shouldDuplicateWithNew)
+ {
+ if (typeParams == null || typeParams.Count == 0)
+ {
+ XmlElement f = (XmlElement)root.SelectSingleNode ("TypeParameters");
+ if (f != null)
+ root.RemoveChild (f);
+ return;
+ }
+ XmlElement e = WriteElement (root, "TypeParameters");
+
+ var nodes = e.SelectNodes ("TypeParameter").Cast<XmlElement> ().ToArray ();
+
+ foreach (GenericParameter t in typeParams)
+ {
+
+ IList<TypeReference> constraints = t.Constraints;
+ GenericParameterAttributes attrs = t.Attributes;
+
+
+ AddXmlNode (
+ nodes,
+ x =>
+ {
+ var baseType = e.SelectSingleNode ("BaseTypeName");
+ // TODO: should this comparison take into account BaseTypeName?
+ return x.GetAttribute ("Name") == t.Name;
+ },
+ x => { }, // no additional action required
+ () =>
+ {
+
+ XmlElement pe = root.OwnerDocument.CreateElement ("TypeParameter");
+ e.AppendChild (pe);
+ pe.SetAttribute ("Name", t.Name);
+ MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""), t.DeclaringType);
+ XmlElement ce = (XmlElement)e.SelectSingleNode ("Constraints");
+ if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
+ {
+ if (ce != null)
+ e.RemoveChild (ce);
+ return pe;
+ }
+ if (ce != null)
+ ce.RemoveAll ();
+ else
+ {
+ ce = root.OwnerDocument.CreateElement ("Constraints");
+ }
+ pe.AppendChild (ce);
+ if ((attrs & GenericParameterAttributes.Contravariant) != 0)
+ AppendElementText (ce, "ParameterAttribute", "Contravariant");
+ if ((attrs & GenericParameterAttributes.Covariant) != 0)
+ AppendElementText (ce, "ParameterAttribute", "Covariant");
+ if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
+ AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
+ if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
+ AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
+ if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
+ AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
+ foreach (TypeReference c in constraints)
+ {
+ TypeDefinition cd = c.Resolve ();
+ AppendElementText (ce,
+ (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
+ GetDocTypeFullName (c));
+ }
+
+ return pe;
+ },
+ member);
+ }
+ }
+
+ private void MakeParameters (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew)
+ {
+ if (mi is MethodDefinition && ((MethodDefinition)mi).IsConstructor)
+ MakeParameters (root, mi, ((MethodDefinition)mi).Parameters, shouldDuplicateWithNew);
+ else if (mi is MethodDefinition)
+ {
+ MethodDefinition mb = (MethodDefinition)mi;
+ IList<ParameterDefinition> parameters = mb.Parameters;
+ MakeParameters (root, mi, parameters, shouldDuplicateWithNew);
+ if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb))
+ {
+ XmlElement p = (XmlElement)root.SelectSingleNode ("Parameters/Parameter[position()=1]");
+ p.SetAttribute ("RefType", "this");
+ }
+ }
+ else if (mi is PropertyDefinition)
+ {
+ IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
+ if (parameters.Count > 0)
+ MakeParameters (root, mi, parameters, shouldDuplicateWithNew);
+ else
+ return;
+ }
+ else if (mi is FieldDefinition) return;
+ else if (mi is EventDefinition) return;
+ else throw new ArgumentException ();
+ }
+
+ internal static string GetDocParameterType (TypeReference type)
+ {
+ return GetDocTypeFullName (type).Replace ("@", "&");
+ }
+
+ private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes, bool shouldDuplicateWithNew = false)
+ {
+ XmlElement e = WriteElement (root, "ReturnValue");
+ var valueToUse = GetDocTypeFullName (type);
+
+ AddXmlNode (e.SelectNodes ("ReturnType").Cast<XmlElement> ().ToArray (),
+ x => x.InnerText == valueToUse,
+ x => x.InnerText = valueToUse,
+ () =>
+ {
+ var newNode = WriteElementText (e, "ReturnType", valueToUse, forceNewElement: true);
+ if (attributes != null)
+ MakeAttributes (e, GetCustomAttributes (attributes, ""), type);
+
+ return newNode;
+ },
+ type);
+ }
+
+ private void MakeReturnValue (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew = false)
+ {
+ if (mi is MethodDefinition && ((MethodDefinition)mi).IsConstructor)
+ return;
+ else if (mi is MethodDefinition)
+ MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes, shouldDuplicateWithNew);
+ else if (mi is PropertyDefinition)
+ MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null, shouldDuplicateWithNew);
+ else if (mi is FieldDefinition)
+ MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null, shouldDuplicateWithNew);
+ else if (mi is EventDefinition)
+ MakeReturnValue (root, ((EventDefinition)mi).EventType, null, shouldDuplicateWithNew);
+ else
+ throw new ArgumentException (mi + " is a " + mi.GetType ().FullName);
+ }
+
+ private XmlElement MakeMember (XmlDocument doc, DocsNodeInfo info, XmlNode members, FrameworkTypeEntry typeEntry)
+ {
+ MemberReference mi = info.Member;
+ if (mi is TypeDefinition) return null;
+
+ string sigs = memberFormatters[0].GetDeclaration (mi);
+ if (sigs == null) return null; // not publicly visible
+
+ // no documentation for property/event accessors. Is there a better way of doing this?
+ if (mi.Name.StartsWith ("get_", StringComparison.Ordinal)) return null;
+ if (mi.Name.StartsWith ("set_", StringComparison.Ordinal)) return null;
+ if (mi.Name.StartsWith ("add_", StringComparison.Ordinal)) return null;
+ if (mi.Name.StartsWith ("remove_", StringComparison.Ordinal)) return null;
+ if (mi.Name.StartsWith ("raise_", StringComparison.Ordinal)) return null;
+
+ XmlElement me = doc.CreateElement ("Member");
+ members.AppendChild (me);
+ me.SetAttribute ("MemberName", GetMemberName (mi));
+
+ info.Node = me;
+ UpdateMember (info, typeEntry);
+ if (exceptions.HasValue &&
+ (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
+ UpdateExceptions (info.Node, info.Member);
+
+ if (since != null)
+ {
+ XmlNode docs = me.SelectSingleNode ("Docs");
+ docs.AppendChild (CreateSinceNode (doc));
+ }
+
+ return me;
+ }
+
+ internal static string GetMemberName (MemberReference mi)
+ {
+ MethodDefinition mb = mi as MethodDefinition;
+ if (mb == null)
+ {
+ PropertyDefinition pi = mi as PropertyDefinition;
+ if (pi == null)
+ return mi.Name;
+ return DocUtils.GetPropertyName (pi);
+ }
+ StringBuilder sb = new StringBuilder (mi.Name.Length);
+ if (!DocUtils.IsExplicitlyImplemented (mb))
+ sb.Append (mi.Name);
+ else
+ {
+ TypeReference iface;
+ MethodReference ifaceMethod;
+ DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
+ sb.Append (GetDocTypeFullName (iface));
+ sb.Append ('.');
+ sb.Append (ifaceMethod.Name);
+ }
+ if (mb.IsGenericMethod ())
+ {
+ IList<GenericParameter> typeParams = mb.GenericParameters;
+ if (typeParams.Count > 0)
+ {
+ sb.Append ("<");
+ sb.Append (typeParams[0].Name);
+ for (int i = 1; i < typeParams.Count; ++i)
+ sb.Append (",").Append (typeParams[i].Name);
+ sb.Append (">");
+ }
+ }
+ return sb.ToString ();
+ }
+
+ /// SIGNATURE GENERATION FUNCTIONS
+ internal static bool IsPrivate (MemberReference mi)
+ {
+ return memberFormatters[0].GetDeclaration (mi) == null;
+ }
+
+ internal static string GetMemberType (MemberReference mi)
+ {
+ if (mi is MethodDefinition && ((MethodDefinition)mi).IsConstructor)
+ return "Constructor";
+ if (mi is MethodDefinition)
+ return "Method";
+ if (mi is PropertyDefinition)
+ return "Property";
+ if (mi is FieldDefinition)
+ return "Field";
+ if (mi is EventDefinition)
+ return "Event";
+ throw new ArgumentException ();
+ }
+
+ private static string GetDocTypeName (TypeReference type)
+ {
+ return docTypeFormatter.GetName (type);
+ }
+
+ internal static string GetDocTypeFullName (TypeReference type)
+ {
+ return DocTypeFullMemberFormatter.Default.GetName (type);
+ }
+
+ internal static string GetXPathForMember (DocumentationMember member)
+ {
+ StringBuilder xpath = new StringBuilder ();
+ xpath.Append ("//Members/Member[@MemberName=\"")
+ .Append (member.MemberName)
+ .Append ("\"]");
+ if (member.Parameters != null && member.Parameters.Count > 0)
+ {
+ xpath.Append ("/Parameters[count(Parameter) = ")
+ .Append (member.Parameters.Count);
+ for (int i = 0; i < member.Parameters.Count; ++i)
+ {
+ xpath.Append (" and Parameter [").Append (i + 1).Append ("]/@Type=\"");
+ xpath.Append (member.Parameters[i]);
+ xpath.Append ("\"");
+ }
+ xpath.Append ("]/..");
+ }
+ return xpath.ToString ();
+ }
+
+ public static string GetXPathForMember (XPathNavigator member)
+ {
+ StringBuilder xpath = new StringBuilder ();
+ xpath.Append ("//Type[@FullName=\"")
+ .Append (member.SelectSingleNode ("../../@FullName").Value)
+ .Append ("\"]/");
+ xpath.Append ("Members/Member[@MemberName=\"")
+ .Append (member.SelectSingleNode ("@MemberName").Value)
+ .Append ("\"]");
+ XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
+ if (parameters.Count > 0)
+ {
+ xpath.Append ("/Parameters[count(Parameter) = ")
+ .Append (parameters.Count);
+ int i = 0;
+ while (parameters.MoveNext ())
+ {
+ ++i;
+ xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
+ xpath.Append (parameters.Current.Value);
+ xpath.Append ("\"");
+ }
+ xpath.Append ("]/..");
+ }
+ return xpath.ToString ();
+ }
+
+ public static string GetXPathForMember (MemberReference member)
+ {
+ StringBuilder xpath = new StringBuilder ();
+ xpath.Append ("//Type[@FullName=\"")
+ .Append (member.DeclaringType.FullName)
+ .Append ("\"]/");
+ xpath.Append ("Members/Member[@MemberName=\"")
+ .Append (GetMemberName (member))
+ .Append ("\"]");
+
+ IList<ParameterDefinition> parameters = null;
+ if (member is MethodDefinition)
+ parameters = ((MethodDefinition)member).Parameters;
+ else if (member is PropertyDefinition)
+ {
+ parameters = ((PropertyDefinition)member).Parameters;
+ }
+ if (parameters != null && parameters.Count > 0)
+ {
+ xpath.Append ("/Parameters[count(Parameter) = ")
+ .Append (parameters.Count);
+ for (int i = 0; i < parameters.Count; ++i)
+ {
+ xpath.Append (" and Parameter [").Append (i + 1).Append ("]/@Type=\"");
+ xpath.Append (GetDocParameterType (parameters[i].ParameterType));
+ xpath.Append ("\"");
+ }
+ xpath.Append ("]/..");
+ }
+ return xpath.ToString ();
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/DocUtils.cs b/mdoc/Mono.Documentation/Updater/DocUtils.cs
new file mode 100644
index 00000000..cec33ccc
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/DocUtils.cs
@@ -0,0 +1,273 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Xml;
+
+using Mono.Cecil;
+
+using Mono.Documentation.Util;
+
+namespace Mono.Documentation.Updater
+{
+ static class DocUtils
+ {
+
+ public static bool DoesNotHaveApiStyle (this XmlElement element, ApiStyle style)
+ {
+ string styleString = style.ToString ().ToLowerInvariant ();
+ string apistylevalue = element.GetAttribute ("apistyle");
+ return apistylevalue != styleString || string.IsNullOrWhiteSpace (apistylevalue);
+ }
+ public static bool HasApiStyle (this XmlElement element, ApiStyle style)
+ {
+ string styleString = style.ToString ().ToLowerInvariant ();
+ return element.GetAttribute ("apistyle") == styleString;
+ }
+ public static bool HasApiStyle (this XmlNode node, ApiStyle style)
+ {
+ var attribute = node.Attributes["apistyle"];
+ return attribute != null && attribute.Value == style.ToString ().ToLowerInvariant ();
+ }
+ public static void AddApiStyle (this XmlElement element, ApiStyle style)
+ {
+ string styleString = style.ToString ().ToLowerInvariant ();
+ var existingValue = element.GetAttribute ("apistyle");
+ if (string.IsNullOrWhiteSpace (existingValue) || existingValue != styleString)
+ {
+ element.SetAttribute ("apistyle", styleString);
+ }
+
+ // Propagate the API style up to the membernode if necessary
+ if (element.LocalName == "AssemblyInfo" && element.ParentNode != null && element.ParentNode.LocalName == "Member")
+ {
+ var member = element.ParentNode;
+ var unifiedAssemblyNode = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']");
+ var classicAssemblyNode = member.SelectSingleNode ("AssemblyInfo[not(@apistyle) or @apistyle='classic']");
+
+ var parentAttribute = element.ParentNode.Attributes["apistyle"];
+ Action removeStyle = () => element.ParentNode.Attributes.Remove (parentAttribute);
+ Action propagateStyle = () =>
+ {
+ if (parentAttribute == null)
+ {
+ // if it doesn't have the attribute, then add it
+ parentAttribute = element.OwnerDocument.CreateAttribute ("apistyle");
+ parentAttribute.Value = styleString;
+ element.ParentNode.Attributes.Append (parentAttribute);
+ }
+ };
+
+ if ((style == ApiStyle.Classic && unifiedAssemblyNode != null) || (style == ApiStyle.Unified && classicAssemblyNode != null))
+ removeStyle ();
+ else
+ propagateStyle ();
+ }
+ }
+ public static void AddApiStyle (this XmlNode node, ApiStyle style)
+ {
+ string styleString = style.ToString ().ToLowerInvariant ();
+ var existingAttribute = node.Attributes["apistyle"];
+ if (existingAttribute == null)
+ {
+ existingAttribute = node.OwnerDocument.CreateAttribute ("apistyle");
+ node.Attributes.Append (existingAttribute);
+ }
+ existingAttribute.Value = styleString;
+ }
+ public static void RemoveApiStyle (this XmlElement element, ApiStyle style)
+ {
+ string styleString = style.ToString ().ToLowerInvariant ();
+ string existingValue = element.GetAttribute ("apistyle");
+ if (string.IsNullOrWhiteSpace (existingValue) || existingValue == styleString)
+ {
+ element.RemoveAttribute ("apistyle");
+ }
+ }
+ public static void RemoveApiStyle (this XmlNode node, ApiStyle style)
+ {
+ var styleAttribute = node.Attributes["apistyle"];
+ if (styleAttribute != null && styleAttribute.Value == style.ToString ().ToLowerInvariant ())
+ {
+ node.Attributes.Remove (styleAttribute);
+ }
+ }
+
+ public static bool IsExplicitlyImplemented (MethodDefinition method)
+ {
+ return method != null && method.IsPrivate && method.IsFinal && method.IsVirtual;
+ }
+
+ public static string GetTypeDotMember (string name)
+ {
+ int startType, startMethod;
+ startType = startMethod = -1;
+ for (int i = 0; i < name.Length; ++i)
+ {
+ if (name[i] == '.')
+ {
+ startType = startMethod;
+ startMethod = i;
+ }
+ }
+ return name.Substring (startType + 1);
+ }
+
+ public static string GetMember (string name)
+ {
+ int i = name.LastIndexOf ('.');
+ if (i == -1)
+ return name;
+ return name.Substring (i + 1);
+ }
+
+ public static void GetInfoForExplicitlyImplementedMethod (
+ MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
+ {
+ iface = null;
+ ifaceMethod = null;
+ if (method.Overrides.Count != 1)
+ throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
+ iface = method.Overrides[0].DeclaringType;
+ ifaceMethod = method.Overrides[0];
+ }
+
+ public static string GetPropertyName (PropertyDefinition pi)
+ {
+ // Issue: (g)mcs-generated assemblies that explicitly implement
+ // properties don't specify the full namespace, just the
+ // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
+ MethodDefinition method = pi.GetMethod;
+ if (method == null)
+ method = pi.SetMethod;
+ if (!IsExplicitlyImplemented (method))
+ return pi.Name;
+
+ // Need to determine appropriate namespace for this member.
+ TypeReference iface;
+ MethodReference ifaceMethod;
+ GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
+ return string.Join (".", new string[]{
+ DocTypeFullMemberFormatter.Default.GetName (iface),
+ GetMember (pi.Name)});
+ }
+
+ public static string GetNamespace (TypeReference type)
+ {
+ if (type.GetElementType ().IsNested)
+ type = type.GetElementType ();
+ while (type != null && type.IsNested)
+ type = type.DeclaringType;
+ if (type == null)
+ return string.Empty;
+
+ string typeNS = type.Namespace;
+
+ // first, make sure this isn't a type reference to another assembly/module
+
+ bool isInAssembly = MDocUpdater.IsInAssemblies (type.Module.Name);
+ if (isInAssembly && !typeNS.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (type))
+ {
+ typeNS = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typeNS);
+ }
+ return typeNS;
+ }
+
+ public static string PathCombine (string dir, string path)
+ {
+ if (dir == null)
+ dir = "";
+ if (path == null)
+ path = "";
+ return Path.Combine (dir, path);
+ }
+
+ public static bool IsExtensionMethod (MethodDefinition method)
+ {
+ return
+ method.CustomAttributes
+ .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
+ && method.DeclaringType.CustomAttributes
+ .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
+ }
+
+ public static bool IsDelegate (TypeDefinition type)
+ {
+ TypeReference baseRef = type.BaseType;
+ if (baseRef == null)
+ return false;
+ return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
+ baseRef.FullName == "System.MulticastDelegate";
+ }
+
+ public static List<TypeReference> GetDeclaringTypes (TypeReference type)
+ {
+ List<TypeReference> decls = new List<TypeReference> ();
+ decls.Add (type);
+ while (type.DeclaringType != null)
+ {
+ decls.Add (type.DeclaringType);
+ type = type.DeclaringType;
+ }
+ decls.Reverse ();
+ return decls;
+ }
+
+ public static int GetGenericArgumentCount (TypeReference type)
+ {
+ GenericInstanceType inst = type as GenericInstanceType;
+ return inst != null
+ ? inst.GenericArguments.Count
+ : type.GenericParameters.Count;
+ }
+
+ public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
+ {
+ HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
+ List<TypeReference> userInterfaces = new List<TypeReference> ();
+ foreach (var ii in type.Interfaces)
+ {
+ var iface = ii.InterfaceType;
+ TypeReference lookup = iface.Resolve () ?? iface;
+ if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
+ userInterfaces.Add (iface);
+ }
+ return userInterfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()));
+ }
+
+ private static string GetQualifiedTypeName (TypeReference type)
+ {
+ return "[" + type.Scope.Name + "]" + type.FullName;
+ }
+
+ private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
+ {
+ HashSet<string> inheritedInterfaces = new HashSet<string> ();
+ Action<TypeDefinition> a = null;
+ a = t =>
+ {
+ if (t == null) return;
+ foreach (var r in t.Interfaces)
+ {
+ inheritedInterfaces.Add (GetQualifiedTypeName (r.InterfaceType));
+ a (r.InterfaceType.Resolve ());
+ }
+ };
+ TypeReference baseRef = type.BaseType;
+ while (baseRef != null)
+ {
+ TypeDefinition baseDef = baseRef.Resolve ();
+ if (baseDef != null)
+ {
+ a (baseDef);
+ baseRef = baseDef.BaseType;
+ }
+ else
+ baseRef = null;
+ }
+ foreach (var r in type.Interfaces)
+ a (r.InterfaceType.Resolve ());
+ return inheritedInterfaces;
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/DocsNodeInfo.cs b/mdoc/Mono.Documentation/Updater/DocsNodeInfo.cs
new file mode 100644
index 00000000..d262fc84
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/DocsNodeInfo.cs
@@ -0,0 +1,105 @@
+using System;
+using System.Collections.Generic;
+using System.Xml;
+
+using Mono.Cecil;
+
+using Mono.Documentation.Util;
+
+namespace Mono.Documentation.Updater
+{
+ class DocsNodeInfo
+ {
+ public DocsNodeInfo (XmlElement node)
+ {
+ this.Node = node;
+ }
+
+ public DocsNodeInfo (XmlElement node, TypeDefinition type)
+ : this (node)
+ {
+ SetType (type);
+ }
+
+ public DocsNodeInfo (XmlElement node, MemberReference member)
+ : this (node)
+ {
+ SetMemberInfo (member);
+ }
+
+ void SetType (TypeDefinition type)
+ {
+ if (type == null)
+ throw new ArgumentNullException ("type");
+ Type = type;
+ GenericParameters = new List<GenericParameter> (type.GenericParameters);
+ List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
+ int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
+ for (int i = 0; i < declTypes.Count - 1; ++i)
+ {
+ int remove = System.Math.Min (maxGenArgs,
+ DocUtils.GetGenericArgumentCount (declTypes[i]));
+ maxGenArgs -= remove;
+ while (remove-- > 0)
+ GenericParameters.RemoveAt (0);
+ }
+ if (DocUtils.IsDelegate (type))
+ {
+ Parameters = type.GetMethod ("Invoke").Parameters;
+ ReturnType = type.GetMethod ("Invoke").ReturnType;
+ ReturnIsReturn = true;
+ }
+ }
+
+ void SetMemberInfo (MemberReference member)
+ {
+ if (member == null)
+ throw new ArgumentNullException ("member");
+ ReturnIsReturn = true;
+ AddRemarks = true;
+ Member = member;
+
+ if (member is MethodReference)
+ {
+ MethodReference mr = (MethodReference)member;
+ Parameters = mr.Parameters;
+ if (mr.IsGenericMethod ())
+ {
+ GenericParameters = new List<GenericParameter> (mr.GenericParameters);
+ }
+ }
+ else if (member is PropertyDefinition)
+ {
+ Parameters = ((PropertyDefinition)member).Parameters;
+ }
+
+ if (member is MethodDefinition)
+ {
+ ReturnType = ((MethodDefinition)member).ReturnType;
+ }
+ else if (member is PropertyDefinition)
+ {
+ ReturnType = ((PropertyDefinition)member).PropertyType;
+ ReturnIsReturn = false;
+ }
+
+ // no remarks section for enum members
+ if (member.DeclaringType != null && ((TypeDefinition)member.DeclaringType).IsEnum)
+ AddRemarks = false;
+ }
+
+ public TypeReference ReturnType;
+ public List<GenericParameter> GenericParameters;
+ public IList<ParameterDefinition> Parameters;
+ public bool ReturnIsReturn;
+ public XmlElement Node;
+ public bool AddRemarks = true;
+ public MemberReference Member;
+ public TypeDefinition Type;
+
+ public override string ToString ()
+ {
+ return string.Format ("{0} - {1} - {2}", Type, Member, Node == null ? "no xml" : "with xml");
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/DocumentationEnumerator.cs b/mdoc/Mono.Documentation/Updater/DocumentationEnumerator.cs
new file mode 100644
index 00000000..53d5e8b0
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/DocumentationEnumerator.cs
@@ -0,0 +1,378 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Xml;
+
+using Mono.Cecil;
+using StringList = System.Collections.Generic.List<string>;
+
+using Mono.Documentation.Util;
+
+namespace Mono.Documentation.Updater
+{
+ class DocumentationEnumerator
+ {
+
+ public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
+ {
+ return GetDocumentationTypes (assembly, forTypes, null);
+ }
+
+ protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
+ {
+ foreach (TypeDefinition type in assembly.GetTypes ())
+ {
+ if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
+ continue;
+ if (seen != null && seen.Contains (type.FullName))
+ continue;
+ yield return type;
+ foreach (TypeDefinition nested in type.NestedTypes)
+ yield return nested;
+ }
+ }
+
+ public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
+ {
+ foreach (XmlElement oldmember in basefile.SelectNodes ("Type/Members/Member"))
+ {
+ if (oldmember.GetAttribute ("__monodocer-seen__") == "true")
+ {
+ oldmember.RemoveAttribute ("__monodocer-seen__");
+ continue;
+ }
+ MemberReference m = GetMember (type, new DocumentationMember (oldmember));
+ if (m == null)
+ {
+ yield return new DocsNodeInfo (oldmember);
+ }
+ else
+ {
+ yield return new DocsNodeInfo (oldmember, m);
+ }
+ }
+ }
+
+ protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
+ {
+ string membertype = member.MemberType;
+
+ string returntype = member.ReturnType;
+
+ string docName = member.MemberName;
+
+ string[] docTypeParams = GetTypeParameters (docName, member.TypeParameters);
+
+ // If we're using 'magic types', then we might get false positives ... in those cases, we keep searching
+ MemberReference likelyCandidate = null;
+
+ // Loop through all members in this type with the same name
+ var reflectedMembers = GetReflectionMembers (type, docName).ToArray ();
+ foreach (MemberReference mi in reflectedMembers)
+ {
+ bool matchedMagicType = false;
+ if (mi is TypeDefinition) continue;
+ if (MDocUpdater.GetMemberType (mi) != membertype) continue;
+
+ if (MDocUpdater.IsPrivate (mi))
+ continue;
+
+ IList<ParameterDefinition> pis = null;
+ string[] typeParams = null;
+ if (mi is MethodDefinition)
+ {
+ MethodDefinition mb = (MethodDefinition)mi;
+ pis = mb.Parameters;
+ if (mb.IsGenericMethod ())
+ {
+ IList<GenericParameter> args = mb.GenericParameters;
+ typeParams = args.Select (p => p.Name).ToArray ();
+ }
+ }
+ else if (mi is PropertyDefinition)
+ pis = ((PropertyDefinition)mi).Parameters;
+
+ // check type parameters
+ int methodTcount = member.TypeParameters == null ? 0 : member.TypeParameters.Count;
+ int reflectionTcount = typeParams == null ? 0 : typeParams.Length;
+ if (methodTcount != reflectionTcount)
+ continue;
+
+ // check member parameters
+ int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
+ int pcount = pis == null ? 0 : pis.Count;
+ if (mcount != pcount)
+ continue;
+
+ MethodDefinition mDef = mi as MethodDefinition;
+ if (mDef != null && !mDef.IsConstructor)
+ {
+ // Casting operators can overload based on return type.
+ string rtype = GetReplacedString (
+ MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
+ typeParams, docTypeParams);
+ string originalRType = rtype;
+ if (MDocUpdater.SwitchingToMagicTypes)
+ {
+ rtype = NativeTypeManager.ConvertFromNativeType (rtype);
+
+ }
+ if ((returntype != rtype && originalRType == rtype) ||
+ (MDocUpdater.SwitchingToMagicTypes && returntype != originalRType && returntype != rtype && originalRType != rtype))
+ {
+ continue;
+ }
+
+ if (originalRType != rtype)
+ matchedMagicType = true;
+ }
+
+ if (pcount == 0)
+ return mi;
+ bool good = true;
+ for (int i = 0; i < pis.Count; i++)
+ {
+ string paramType = GetReplacedString (
+ MDocUpdater.GetDocParameterType (pis[i].ParameterType),
+ typeParams, docTypeParams);
+
+ // if magictypes, replace paramType to "classic value" ... so the comparison works
+ string originalParamType = paramType;
+ if (MDocUpdater.SwitchingToMagicTypes)
+ {
+ paramType = NativeTypeManager.ConvertFromNativeType (paramType);
+ }
+
+ string xmlMemberType = member.Parameters[i];
+ if ((!paramType.Equals (xmlMemberType) && paramType.Equals (originalParamType)) ||
+ (MDocUpdater.SwitchingToMagicTypes && !originalParamType.Equals (xmlMemberType) && !paramType.Equals (xmlMemberType) && !paramType.Equals (originalParamType)))
+ {
+
+ // did not match ... if we're dropping the namespace, and the paramType has the dropped
+ // namespace, we should see if it matches when added
+ bool stillDoesntMatch = true;
+ if (MDocUpdater.HasDroppedNamespace (type) && paramType.StartsWith (MDocUpdater.droppedNamespace))
+ {
+ string withDroppedNs = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, xmlMemberType);
+
+ stillDoesntMatch = withDroppedNs != paramType;
+ }
+
+ if (stillDoesntMatch)
+ {
+ good = false;
+ break;
+ }
+ }
+
+ if (originalParamType != paramType)
+ matchedMagicType = true;
+ }
+ if (!good) continue;
+
+ if (MDocUpdater.SwitchingToMagicTypes && likelyCandidate == null && matchedMagicType)
+ {
+ // 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
+ likelyCandidate = mi;
+ continue;
+ }
+
+ return mi;
+ }
+
+ return likelyCandidate;
+ }
+
+ static string[] GetTypeParameters (string docName, IEnumerable<string> knownParameters)
+ {
+ if (docName[docName.Length - 1] != '>')
+ return null;
+ StringList types = new StringList ();
+ int endToken = docName.Length - 2;
+ int i = docName.Length - 2;
+ do
+ {
+ if (docName[i] == ',' || docName[i] == '<')
+ {
+ types.Add (docName.Substring (i + 1, endToken - i));
+ endToken = i - 1;
+ }
+ if (docName[i] == '<')
+ break;
+ } while (--i >= 0);
+
+ types.Reverse ();
+ var arrayTypes = types.ToArray ();
+
+ if (knownParameters != null && knownParameters.Any () && arrayTypes.Length != knownParameters.Count ())
+ return knownParameters.ToArray ();
+ else
+ return arrayTypes;
+ }
+
+ protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
+ {
+ // In case of dropping the namespace, we have to remove the dropped NS
+ // so that docName will match what's in the assembly/type
+ if (MDocUpdater.HasDroppedNamespace (type) && docName.StartsWith (MDocUpdater.droppedNamespace + "."))
+ {
+ int droppedNsLength = MDocUpdater.droppedNamespace.Length;
+ docName = docName.Substring (droppedNsLength + 1, docName.Length - droppedNsLength - 1);
+ }
+
+ // need to worry about 4 forms of //@MemberName values:
+ // 1. "Normal" (non-generic) member names: GetEnumerator
+ // - Lookup as-is.
+ // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
+ // - try as-is, and try type.member (due to "kludge" for property
+ // support.
+ // 3. "Normal" Generic member names: Sort<T> (CSC)
+ // - need to remove generic parameters --> "Sort"
+ // 4. Explicitly-implemented interface members for generic interfaces:
+ // -- System.Collections.Generic.IEnumerable<T>.Current
+ // - Try as-is, and try type.member, *keeping* the generic parameters.
+ // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
+ // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
+ // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
+ // this as (1) or (2).
+ if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1)
+ {
+ // Cases 1 & 2
+ foreach (MemberReference mi in type.GetMembers (docName))
+ yield return mi;
+ if (CountChars (docName, '.') > 0)
+ {
+
+ Func<MemberReference, bool> verifyInterface = (member) =>
+ {
+ var meth = member as MethodDefinition;
+
+ if (meth == null && member is PropertyReference)
+ {
+ var propertyDefinition = ((PropertyReference)member).Resolve ();
+ meth = propertyDefinition.GetMethod ?? propertyDefinition.SetMethod;
+ }
+ return meth != null && (member.Name.Equals (".ctor") || DocUtils.IsExplicitlyImplemented (meth));
+ };
+
+ int memberCount = 0;
+
+ // might be a property; try only type.member instead of
+ // namespace.type.member.
+ var typeMember = DocUtils.GetTypeDotMember (docName);
+ var memberName = DocUtils.GetMember (docName);
+ foreach (MemberReference mi in
+ type.GetMembers (typeMember).Where (verifyInterface))
+ {
+ memberCount++;
+ yield return mi;
+ }
+
+ // some VB libraries use just the member name
+ foreach (MemberReference mi in
+ type.GetMembers (memberName).Where (verifyInterface))
+ {
+ memberCount++;
+ yield return mi;
+ }
+
+ // some VB libraries use a `typemember` naming convention
+ foreach (MemberReference mi in
+ type.GetMembers (typeMember.Replace (".", "")).Where (verifyInterface))
+ {
+ memberCount++;
+ yield return mi;
+ }
+
+ // if we still haven't found the member, there are some VB libraries
+ // that use a different interface name for implementation.
+ if (memberCount == 0)
+ {
+ foreach (MemberReference mi in
+ type
+ .GetMembers ()
+ .Where (m => m.Name.StartsWith ("I", StringComparison.InvariantCultureIgnoreCase) && m.Name.EndsWith (memberName, StringComparison.InvariantCultureIgnoreCase))
+ .Where (verifyInterface))
+ yield return mi;
+ }
+ }
+ yield break;
+ }
+ // cases 3 & 4
+ int numLt = 0;
+ int numDot = 0;
+ int startLt, startType, startMethod;
+ startLt = startType = startMethod = -1;
+ for (int i = 0; i < docName.Length; ++i)
+ {
+ switch (docName[i])
+ {
+ case '<':
+ if (numLt == 0)
+ {
+ startLt = i;
+ }
+ ++numLt;
+ break;
+ case '>':
+ --numLt;
+ if (numLt == 0 && (i + 1) < docName.Length)
+ // there's another character in docName, so this <...> sequence is
+ // probably part of a generic type -- case 4.
+ startLt = -1;
+ break;
+ case '.':
+ startType = startMethod;
+ startMethod = i;
+ ++numDot;
+ break;
+ }
+ }
+ string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
+ // case 3
+ foreach (MemberReference mi in type.GetMembers (refName))
+ yield return mi;
+
+ // case 4
+ foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
+ yield return mi;
+
+ // If we _still_ haven't found it, we've hit another generic naming issue:
+ // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
+ // explicitly-implemented METHOD names (not properties), e.g.
+ // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
+ // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
+ // which the XML docs will contain.
+ //
+ // Alas, we can't derive the Mono name from docName, so we need to iterate
+ // over all member names, convert them into CSC format, and compare... :-(
+ if (numDot == 0)
+ yield break;
+ foreach (MemberReference mi in type.GetMembers ())
+ {
+ if (MDocUpdater.GetMemberName (mi) == docName)
+ yield return mi;
+ }
+ }
+
+ static string GetReplacedString (string typeName, string[] from, string[] to)
+ {
+ if (from == null)
+ return typeName;
+ for (int i = 0; i < from.Length; ++i)
+ typeName = typeName.Replace (from[i], to[i]);
+ return typeName;
+ }
+
+ private static int CountChars (string s, char c)
+ {
+ int count = 0;
+ for (int i = 0; i < s.Length; ++i)
+ {
+ if (s[i] == c)
+ ++count;
+ }
+ return count;
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/DocumentationImporter.cs b/mdoc/Mono.Documentation/Updater/DocumentationImporter.cs
new file mode 100644
index 00000000..7c448ca4
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/DocumentationImporter.cs
@@ -0,0 +1,8 @@
+namespace Mono.Documentation.Updater
+{
+ abstract class DocumentationImporter
+ {
+
+ public abstract void ImportDocumentation (DocsNodeInfo info);
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/DocumentationMember.cs b/mdoc/Mono.Documentation/Updater/DocumentationMember.cs
new file mode 100644
index 00000000..c581daeb
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/DocumentationMember.cs
@@ -0,0 +1,151 @@
+using System;
+using System.Xml;
+using StringList = System.Collections.Generic.List<string>;
+using StringToStringMap = System.Collections.Generic.Dictionary<string, string>;
+
+namespace Mono.Documentation.Updater
+{
+ class DocumentationMember
+ {
+ public StringToStringMap MemberSignatures = new StringToStringMap ();
+ public string ReturnType;
+ public StringList Parameters;
+ public StringList TypeParameters;
+ public string MemberName;
+ public string MemberType;
+
+ /// <summary>Removes modreq and modopt from ReturnType, Parameters, and TypeParameters</summary>
+ private void CleanTypes ()
+ {
+ ReturnType = MemberFormatter.RemoveMod (ReturnType);
+ MemberType = MemberFormatter.RemoveMod (MemberType);
+
+ if (Parameters != null)
+ {
+ for (var i = 0; i < Parameters.Count; i++)
+ Parameters[i] = MemberFormatter.RemoveMod (Parameters[i]);
+ }
+
+ if (TypeParameters != null)
+ {
+ for (var i = 0; i < TypeParameters.Count; i++)
+ TypeParameters[i] = MemberFormatter.RemoveMod (TypeParameters[i]);
+ }
+ }
+
+ public DocumentationMember (XmlReader reader)
+ {
+ MemberName = reader.GetAttribute ("MemberName");
+ int depth = reader.Depth;
+ bool go = true;
+ StringList p = new StringList ();
+ StringList tp = new StringList ();
+ do
+ {
+ if (reader.NodeType != XmlNodeType.Element)
+ continue;
+
+ bool shouldUse = true;
+ try
+ {
+ string apistyle = reader.GetAttribute ("apistyle");
+ shouldUse = string.IsNullOrWhiteSpace (apistyle) || apistyle == "classic"; // only use this tag if it's an 'classic' style node
+ }
+ catch (Exception ex) { }
+ switch (reader.Name)
+ {
+ case "MemberSignature":
+ if (shouldUse)
+ {
+ MemberSignatures[reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
+ }
+ break;
+ case "MemberType":
+ MemberType = reader.ReadElementString ();
+ break;
+ case "ReturnType":
+ if (reader.Depth == depth + 2 && shouldUse)
+ ReturnType = reader.ReadElementString ();
+ break;
+ case "Parameter":
+ if (reader.Depth == depth + 2 && shouldUse)
+ p.Add (reader.GetAttribute ("Type"));
+ break;
+ case "TypeParameter":
+ if (reader.Depth == depth + 2 && shouldUse)
+ tp.Add (reader.GetAttribute ("Name"));
+ break;
+ case "Docs":
+ if (reader.Depth == depth + 1)
+ go = false;
+ break;
+ }
+ } while (go && reader.Read () && reader.Depth >= depth);
+ if (p.Count > 0)
+ {
+ Parameters = p;
+ }
+ if (tp.Count > 0)
+ {
+ TypeParameters = tp;
+ }
+ else
+ {
+ DiscernTypeParameters ();
+ }
+
+ CleanTypes ();
+ }
+
+ public DocumentationMember (XmlNode node)
+ {
+ MemberName = node.Attributes["MemberName"].Value;
+ foreach (XmlNode n in node.SelectNodes ("MemberSignature"))
+ {
+ XmlAttribute l = n.Attributes["Language"];
+ XmlAttribute v = n.Attributes["Value"];
+ XmlAttribute apistyle = n.Attributes["apistyle"];
+ bool shouldUse = apistyle == null || apistyle.Value == "classic";
+ if (l != null && v != null && shouldUse)
+ MemberSignatures[l.Value] = v.Value;
+ }
+ MemberType = node.SelectSingleNode ("MemberType").InnerText;
+ XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType[not(@apistyle) or @apistyle='classic']");
+ if (rt != null)
+ ReturnType = rt.InnerText;
+ XmlNodeList p = node.SelectNodes ("Parameters/Parameter[not(@apistyle) or @apistyle='classic']");
+ if (p.Count > 0)
+ {
+ Parameters = new StringList (p.Count);
+ for (int i = 0; i < p.Count; ++i)
+ Parameters.Add (p[i].Attributes["Type"].Value);
+ }
+ XmlNodeList tp = node.SelectNodes ("TypeParameters/TypeParameter[not(@apistyle) or @apistyle='classic']");
+ if (tp.Count > 0)
+ {
+ TypeParameters = new StringList (tp.Count);
+ for (int i = 0; i < tp.Count; ++i)
+ TypeParameters.Add (tp[i].Attributes["Name"].Value);
+ }
+ else
+ {
+ DiscernTypeParameters ();
+ }
+
+ CleanTypes ();
+ }
+
+ void DiscernTypeParameters ()
+ {
+ // see if we can discern the param list from the name
+ if (MemberName.Contains ("<") && MemberName.EndsWith (">"))
+ {
+ var starti = MemberName.IndexOf ("<") + 1;
+ var endi = MemberName.LastIndexOf (">");
+ var paramlist = MemberName.Substring (starti, endi - starti);
+ var tparams = paramlist.Split (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+ TypeParameters = new StringList (tparams);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/DynamicParserContext.cs b/mdoc/Mono.Documentation/Updater/DynamicParserContext.cs
new file mode 100644
index 00000000..898ecaf9
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/DynamicParserContext.cs
@@ -0,0 +1,30 @@
+using System.Collections.ObjectModel;
+using System.Linq;
+
+using Mono.Cecil;
+
+using Mono.Documentation.Util;
+
+namespace Mono.Documentation.Updater
+{
+ public class DynamicParserContext
+ {
+ public ReadOnlyCollection<bool> TransformFlags;
+ public int TransformIndex;
+
+ public DynamicParserContext (ICustomAttributeProvider provider)
+ {
+ CustomAttribute da;
+ if (provider.HasCustomAttributes &&
+ (da = (provider.CustomAttributes.Cast<CustomAttribute> ()
+ .SingleOrDefault (ca => ca.GetDeclaringType () == "System.Runtime.CompilerServices.DynamicAttribute"))) != null)
+ {
+ CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
+ ? new CustomAttributeArgument[0]
+ : (CustomAttributeArgument[])da.ConstructorArguments[0].Value;
+
+ TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool)t.Value).ToArray ());
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/EcmaDocumentationEnumerator.cs b/mdoc/Mono.Documentation/Updater/EcmaDocumentationEnumerator.cs
new file mode 100644
index 00000000..86cce337
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/EcmaDocumentationEnumerator.cs
@@ -0,0 +1,169 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Xml;
+
+using Mono.Cecil;
+
+using Mono.Documentation.Util;
+
+namespace Mono.Documentation.Updater
+{
+ class EcmaDocumentationEnumerator : DocumentationEnumerator
+ {
+
+ XmlReader ecmadocs;
+ MDocUpdater app;
+
+ public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
+ {
+ this.app = app;
+ this.ecmadocs = ecmaDocs;
+ }
+
+ public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
+ {
+ HashSet<string> seen = new HashSet<string> ();
+ return GetDocumentationTypes (assembly, forTypes, seen)
+ .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
+ }
+
+ new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
+ {
+ int typeDepth = -1;
+ while (ecmadocs.Read ())
+ {
+ switch (ecmadocs.Name)
+ {
+ case "Type":
+ {
+ if (typeDepth == -1)
+ typeDepth = ecmadocs.Depth;
+ if (ecmadocs.NodeType != XmlNodeType.Element)
+ continue;
+ if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
+ continue;
+ string typename = ecmadocs.GetAttribute ("FullName");
+ string typename2 = MDocUpdater.GetTypeFileName (typename);
+ if (forTypes != null &&
+ forTypes.BinarySearch (typename) < 0 &&
+ typename != typename2 &&
+ forTypes.BinarySearch (typename2) < 0)
+ continue;
+ TypeDefinition t;
+ if ((t = assembly.GetType (typename)) == null &&
+ (t = assembly.GetType (typename2)) == null)
+ continue;
+ seen.Add (typename);
+ if (typename != typename2)
+ seen.Add (typename2);
+ Console.WriteLine (" Import: {0}", t.FullName);
+ if (ecmadocs.Name != "Docs")
+ {
+ int depth = ecmadocs.Depth;
+ while (ecmadocs.Read ())
+ {
+ if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
+ break;
+ }
+ }
+ if (!ecmadocs.IsStartElement ("Docs"))
+ throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
+ yield return t;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
+ {
+ return GetMembers (basefile, type)
+ .Concat (base.GetDocumentationMembers (basefile, type));
+ }
+
+ private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
+ {
+ while (ecmadocs.Name != "Members" && ecmadocs.Read ())
+ {
+ // do nothing
+ }
+ if (ecmadocs.IsEmptyElement)
+ yield break;
+
+ int membersDepth = ecmadocs.Depth;
+ bool go = true;
+ while (go && ecmadocs.Read ())
+ {
+ switch (ecmadocs.Name)
+ {
+ case "Member":
+ {
+ if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
+ continue;
+ DocumentationMember dm = new DocumentationMember (ecmadocs);
+
+ string xp = MDocUpdater.GetXPathForMember (dm);
+ XmlElement oldmember = (XmlElement)basefile.SelectSingleNode (xp);
+ MemberReference m;
+ if (oldmember == null)
+ {
+ m = GetMember (type, dm);
+ if (m == null)
+ {
+ app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
+ type.FullName, dm.MemberSignatures["C#"]);
+ // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
+ continue;
+ }
+ // oldmember lookup may have failed due to type parameter renames.
+ // Try again.
+ oldmember = (XmlElement)basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
+ if (oldmember == null)
+ {
+ XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
+ oldmember = basefile.CreateElement ("Member");
+ oldmember.SetAttribute ("MemberName", dm.MemberName);
+ members.AppendChild (oldmember);
+ foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys))
+ {
+ XmlElement ms = basefile.CreateElement ("MemberSignature");
+ ms.SetAttribute ("Language", key);
+ ms.SetAttribute ("Value", (string)dm.MemberSignatures[key]);
+ oldmember.AppendChild (ms);
+ }
+ oldmember.SetAttribute ("__monodocer-seen__", "true");
+ Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").InnerText);
+ app.additions++;
+ }
+ }
+ else
+ {
+ m = GetMember (type, new DocumentationMember (oldmember));
+ if (m == null)
+ {
+ app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
+ type.FullName, dm.MemberSignatures["C#"]);
+ continue;
+ }
+ oldmember.SetAttribute ("__monodocer-seen__", "true");
+ }
+ DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
+ if (ecmadocs.Name != "Docs")
+ throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
+ yield return node;
+ break;
+ }
+ case "Members":
+ if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement)
+ {
+ go = false;
+ }
+ break;
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/EcmaDocumentationImporter.cs b/mdoc/Mono.Documentation/Updater/EcmaDocumentationImporter.cs
new file mode 100644
index 00000000..34210577
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/EcmaDocumentationImporter.cs
@@ -0,0 +1,118 @@
+using System;
+using System.Xml;
+
+using StringList = System.Collections.Generic.List<string>;
+
+namespace Mono.Documentation.Updater
+{
+ class EcmaDocumentationImporter : DocumentationImporter
+ {
+
+ XmlReader ecmadocs;
+
+ public EcmaDocumentationImporter (XmlReader ecmaDocs)
+ {
+ this.ecmadocs = ecmaDocs;
+ }
+
+ public override void ImportDocumentation (DocsNodeInfo info)
+ {
+ if (!ecmadocs.IsStartElement ("Docs"))
+ {
+ return;
+ }
+
+ XmlElement e = info.Node;
+
+ int depth = ecmadocs.Depth;
+ ecmadocs.ReadStartElement ("Docs");
+ while (ecmadocs.Read ())
+ {
+ if (ecmadocs.Name == "Docs")
+ {
+ if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
+ break;
+ else
+ throw new InvalidOperationException ("Skipped past current <Docs/> element!");
+ }
+ if (!ecmadocs.IsStartElement ())
+ continue;
+ switch (ecmadocs.Name)
+ {
+ case "param":
+ case "typeparam":
+ {
+ string name = ecmadocs.GetAttribute ("name");
+ if (name == null)
+ break;
+ XmlNode doc = e.SelectSingleNode (
+ ecmadocs.Name + "[@name='" + name + "']");
+ string value = ecmadocs.ReadInnerXml ();
+ if (doc != null)
+ doc.InnerXml = value.Replace ("\r", "");
+ break;
+ }
+ case "altmember":
+ case "exception":
+ case "permission":
+ case "seealso":
+ {
+ string name = ecmadocs.Name;
+ string cref = ecmadocs.GetAttribute ("cref");
+ if (cref == null)
+ break;
+ XmlNode doc = e.SelectSingleNode (
+ ecmadocs.Name + "[@cref='" + cref + "']");
+ string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
+ if (doc != null)
+ doc.InnerXml = value;
+ else
+ {
+ XmlElement n = e.OwnerDocument.CreateElement (name);
+ n.SetAttribute ("cref", cref);
+ n.InnerXml = value;
+ e.AppendChild (n);
+ }
+ break;
+ }
+ default:
+ {
+ string name = ecmadocs.Name;
+ string xpath = ecmadocs.Name;
+ StringList attributes = new StringList (ecmadocs.AttributeCount);
+ if (ecmadocs.MoveToFirstAttribute ())
+ {
+ do
+ {
+ attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
+ } while (ecmadocs.MoveToNextAttribute ());
+ ecmadocs.MoveToContent ();
+ }
+ if (attributes.Count > 0)
+ {
+ xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
+ }
+ XmlNode doc = e.SelectSingleNode (xpath);
+ string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
+ if (doc != null)
+ {
+ doc.InnerXml = value;
+ }
+ else
+ {
+ XmlElement n = e.OwnerDocument.CreateElement (name);
+ n.InnerXml = value;
+ foreach (string a in attributes)
+ {
+ int eq = a.IndexOf ('=');
+ n.SetAttribute (a.Substring (1, eq - 1), a.Substring (eq + 2, a.Length - eq - 3));
+ }
+ e.AppendChild (n);
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/Formatters/ApplePlatformEnumFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/ApplePlatformEnumFormatter.cs
new file mode 100644
index 00000000..ec3485df
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/Formatters/ApplePlatformEnumFormatter.cs
@@ -0,0 +1,95 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using Mono.Cecil;
+
+namespace Mono.Documentation.Updater
+{
+ /// <summary>A custom formatter for the ObjCRuntime.Platform enumeration.</summary>
+ class ApplePlatformEnumFormatter : AttributeValueFormatter
+ {
+ public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
+ {
+ TypeReference valueType = type.Reference;
+ string typename = MDocUpdater.GetDocTypeFullName (valueType);
+ TypeDefinition valueDef = type.Definition;
+ if (typename.Contains ("ObjCRuntime.Platform") && valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute"))
+ {
+
+ var values = MDocUpdater.GetEnumerationValues (valueDef);
+ long c = MDocUpdater.ToInt64 (v);
+
+ returnvalue = Format (c, values, typename);
+ return true;
+ }
+
+ returnvalue = null;
+ return false;
+ }
+
+ string Format (long c, IDictionary<long, string> values, string typename)
+ {
+ int iosarch, iosmajor, iosminor, iossubminor;
+ int macarch, macmajor, macminor, macsubminor;
+ GetEncodingiOS (c, out iosarch, out iosmajor, out iosminor, out iossubminor);
+ GetEncodingMac ((ulong)c, out macarch, out macmajor, out macminor, out macsubminor);
+
+ if (iosmajor == 0 & iosminor == 0 && iossubminor == 0)
+ {
+ return FormatValues ("Mac", macarch, macmajor, macminor, macsubminor);
+ }
+
+ if (macmajor == 0 & macminor == 0 && macsubminor == 0)
+ {
+ return FormatValues ("iOS", iosarch, iosmajor, iosminor, iossubminor);
+ }
+
+ return string.Format ("(Platform){0}", c);
+ }
+
+ string FormatValues (string plat, int arch, int major, int minor, int subminor)
+ {
+ string archstring = "";
+ switch (arch)
+ {
+ case 1:
+ archstring = "32";
+ break;
+ case 2:
+ archstring = "64";
+ break;
+ }
+ return string.Format ("Platform.{4}_{0}_{1}{2} | Platform.{4}_Arch{3}",
+ major,
+ minor,
+ subminor == 0 ? "" : "_" + subminor.ToString (),
+ archstring,
+ plat
+ );
+ }
+
+ void GetEncodingiOS (long entireLong, out int archindex, out int major, out int minor, out int subminor)
+ {
+ long lowerBits = entireLong & 0xffffffff;
+ int lowerBitsAsInt = (int)lowerBits;
+ GetEncoding (lowerBitsAsInt, out archindex, out major, out minor, out subminor);
+ }
+
+ void GetEncodingMac (ulong entireLong, out int archindex, out int major, out int minor, out int subminor)
+ {
+ ulong higherBits = entireLong & 0xffffffff00000000;
+ int higherBitsAsInt = (int)((higherBits) >> 32);
+ GetEncoding (higherBitsAsInt, out archindex, out major, out minor, out subminor);
+ }
+
+ void GetEncoding (Int32 encodedBits, out int archindex, out int major, out int minor, out int subminor)
+ {
+ // format is AAJJNNSS
+ archindex = (int)((encodedBits & 0xFF000000) >> 24);
+ major = (int)((encodedBits & 0x00FF0000) >> 16);
+ minor = (int)((encodedBits & 0x0000FF00) >> 8);
+ subminor = (int)((encodedBits & 0x000000FF) >> 0);
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/Formatters/AttributeValueFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/AttributeValueFormatter.cs
new file mode 100644
index 00000000..4c41f9a7
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/Formatters/AttributeValueFormatter.cs
@@ -0,0 +1,66 @@
+using System;
+
+using Mono.Cecil;
+
+using Mono.Documentation.Util;
+
+namespace Mono.Documentation.Updater
+{
+ /// <summary>Formats attribute values. Should return true if it is able to format the value.</summary>
+ class AttributeValueFormatter
+ {
+ public virtual bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
+ {
+ TypeReference valueType = type.Reference;
+ if (v == null)
+ {
+ returnvalue = "null";
+ return true;
+ }
+ if (valueType.FullName == "System.Type")
+ {
+ var vTypeRef = v as TypeReference;
+ if (vTypeRef != null)
+ returnvalue = "typeof(" + NativeTypeManager.GetTranslatedName (vTypeRef) + ")"; // TODO: drop NS handling
+ else
+ returnvalue = "typeof(" + v.ToString () + ")";
+
+ return true;
+ }
+ if (valueType.FullName == "System.String")
+ {
+ returnvalue = "\"" + v.ToString () + "\"";
+ return true;
+ }
+ if (valueType.FullName == "System.Char")
+ {
+ returnvalue = "'" + v.ToString () + "'";
+ return true;
+ }
+ if (v is Boolean)
+ {
+ returnvalue = (bool)v ? "true" : "false";
+ return true;
+ }
+
+ TypeDefinition valueDef = type.Definition;
+ if (valueDef == null || !valueDef.IsEnum)
+ {
+ returnvalue = v.ToString ();
+ return true;
+ }
+
+ string typename = MDocUpdater.GetDocTypeFullName (valueType);
+ var values = MDocUpdater.GetEnumerationValues (valueDef);
+ long c = MDocUpdater.ToInt64 (v);
+ if (values.ContainsKey (c))
+ {
+ returnvalue = typename + "." + values[c];
+ return true;
+ }
+
+ returnvalue = null;
+ return false;
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/Formatters/CSharpFullMemberFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/CSharpFullMemberFormatter.cs
new file mode 100644
index 00000000..5e89539d
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/Formatters/CSharpFullMemberFormatter.cs
@@ -0,0 +1,659 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+
+using Mono.Cecil;
+
+using Mono.Documentation.Util;
+
+namespace Mono.Documentation.Updater
+{
+ public class CSharpFullMemberFormatter : MemberFormatter
+ {
+
+ public override string Language
+ {
+ get { return "C#"; }
+ }
+
+ protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
+ {
+
+ string ns = DocUtils.GetNamespace (type);
+ if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
+ buf.Append (ns).Append ('.');
+ return buf;
+ }
+
+ protected virtual string GetCSharpType (string t)
+ {
+ // make sure there are no modifiers in the type string (add them back before returning)
+ string typeToCompare = t;
+ string[] splitType = null;
+ if (t.Contains (' '))
+ {
+ splitType = t.Split (' ');
+ typeToCompare = splitType[0];
+ }
+
+ switch (typeToCompare)
+ {
+ case "System.Byte": typeToCompare = "byte"; break;
+ case "System.SByte": typeToCompare = "sbyte"; break;
+ case "System.Int16": typeToCompare = "short"; break;
+ case "System.Int32": typeToCompare = "int"; break;
+ case "System.Int64": typeToCompare = "long"; break;
+
+ case "System.UInt16": typeToCompare = "ushort"; break;
+ case "System.UInt32": typeToCompare = "uint"; break;
+ case "System.UInt64": typeToCompare = "ulong"; break;
+
+ case "System.Single": typeToCompare = "float"; break;
+ case "System.Double": typeToCompare = "double"; break;
+ case "System.Decimal": typeToCompare = "decimal"; break;
+ case "System.Boolean": typeToCompare = "bool"; break;
+ case "System.Char": typeToCompare = "char"; break;
+ case "System.Void": typeToCompare = "void"; break;
+ case "System.String": typeToCompare = "string"; break;
+ case "System.Object": typeToCompare = "object"; break;
+ }
+
+ if (splitType != null)
+ {
+ // re-add modreq/modopt if it was there
+ splitType[0] = typeToCompare;
+ typeToCompare = string.Join (" ", splitType);
+ }
+ return typeToCompare == t ? null : typeToCompare;
+ }
+
+ protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
+ {
+ if (context != null && context.TransformFlags != null &&
+ (context.TransformFlags.Count == 0 || context.TransformFlags[context.TransformIndex]))
+ {
+ context.TransformIndex++;
+ return buf.Append ("dynamic");
+ }
+
+ if (type is GenericParameter)
+ return AppendGenericParameterConstraints (buf, (GenericParameter)type, context).Append (type.Name);
+ string t = type.FullName;
+ if (!t.StartsWith ("System."))
+ {
+ return base.AppendTypeName (buf, type, context);
+ }
+
+ string s = GetCSharpType (t);
+ if (s != null)
+ {
+ if (context != null)
+ context.TransformIndex++;
+ return buf.Append (s);
+ }
+
+ return base.AppendTypeName (buf, type, context);
+ }
+
+ private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
+ {
+ if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
+ return buf;
+ GenericParameterAttributes attrs = type.Attributes;
+ bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
+ bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
+ if (isin)
+ buf.Append ("in ");
+ else if (isout)
+ buf.Append ("out ");
+ return buf;
+ }
+
+ protected override string GetTypeDeclaration (TypeDefinition type)
+ {
+ string visibility = GetTypeVisibility (type.Attributes);
+ if (visibility == null)
+ return null;
+
+ StringBuilder buf = new StringBuilder ();
+
+ buf.Append (visibility);
+ buf.Append (" ");
+
+ MemberFormatter full = new CSharpFullMemberFormatter ();
+
+ if (DocUtils.IsDelegate (type))
+ {
+ buf.Append ("delegate ");
+ MethodDefinition invoke = type.GetMethod ("Invoke");
+ buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
+ buf.Append (GetName (type));
+ AppendParameters (buf, invoke, invoke.Parameters);
+ AppendGenericTypeConstraints (buf, type);
+ buf.Append (";");
+
+ return buf.ToString ();
+ }
+
+ if (type.IsAbstract && !type.IsInterface)
+ buf.Append ("abstract ");
+ if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
+ buf.Append ("sealed ");
+ buf.Replace ("abstract sealed", "static");
+
+ buf.Append (GetTypeKind (type));
+ buf.Append (" ");
+ buf.Append (GetCSharpType (type.FullName) == null
+ ? GetName (type)
+ : type.Name);
+
+ if (!type.IsEnum)
+ {
+ TypeReference basetype = type.BaseType;
+ if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
+ basetype = null;
+
+ List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
+ .Select (iface => full.GetName (iface))
+ .OrderBy (s => s)
+ .ToList ();
+
+ if (basetype != null || interface_names.Count > 0)
+ buf.Append (" : ");
+
+ if (basetype != null)
+ {
+ buf.Append (full.GetName (basetype));
+ if (interface_names.Count > 0)
+ buf.Append (", ");
+ }
+
+ for (int i = 0; i < interface_names.Count; i++)
+ {
+ if (i != 0)
+ buf.Append (", ");
+ buf.Append (interface_names[i]);
+ }
+ AppendGenericTypeConstraints (buf, type);
+ }
+
+ return buf.ToString ();
+ }
+
+ static string GetTypeKind (TypeDefinition t)
+ {
+ if (t.IsEnum)
+ return "enum";
+ if (t.IsValueType)
+ return "struct";
+ if (t.IsClass || t.FullName == "System.Enum")
+ return "class";
+ if (t.IsInterface)
+ return "interface";
+ throw new ArgumentException (t.FullName);
+ }
+
+ static string GetTypeVisibility (TypeAttributes ta)
+ {
+ switch (ta & TypeAttributes.VisibilityMask)
+ {
+ case TypeAttributes.Public:
+ case TypeAttributes.NestedPublic:
+ return "public";
+
+ case TypeAttributes.NestedFamily:
+ case TypeAttributes.NestedFamORAssem:
+ return "protected";
+
+ default:
+ return null;
+ }
+ }
+
+ protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
+ {
+ if (type.GenericParameters.Count == 0)
+ return buf;
+ return AppendConstraints (buf, type.GenericParameters);
+ }
+
+ private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
+ {
+ foreach (GenericParameter genArg in genArgs)
+ {
+ GenericParameterAttributes attrs = genArg.Attributes;
+ IList<TypeReference> constraints = genArg.Constraints;
+ if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
+ continue;
+
+ bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
+ bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
+ bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
+ bool comma = false;
+
+ if (!isref && !isvt && !isnew && constraints.Count == 0)
+ continue;
+ buf.Append (" where ").Append (genArg.Name).Append (" : ");
+ if (isref)
+ {
+ buf.Append ("class");
+ comma = true;
+ }
+ else if (isvt)
+ {
+ buf.Append ("struct");
+ comma = true;
+ }
+ if (constraints.Count > 0 && !isvt)
+ {
+ if (comma)
+ buf.Append (", ");
+ buf.Append (GetTypeName (constraints[0]));
+ for (int i = 1; i < constraints.Count; ++i)
+ buf.Append (", ").Append (GetTypeName (constraints[i]));
+ }
+ if (isnew && !isvt)
+ {
+ if (comma)
+ buf.Append (", ");
+ buf.Append ("new()");
+ }
+ }
+ return buf;
+ }
+
+ protected override string GetConstructorDeclaration (MethodDefinition constructor)
+ {
+ StringBuilder buf = new StringBuilder ();
+ AppendVisibility (buf, constructor);
+ if (buf.Length == 0)
+ return null;
+
+ buf.Append (' ');
+ base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
+ AppendParameters (buf, constructor, constructor.Parameters);
+ buf.Append (';');
+
+ return buf.ToString ();
+ }
+
+ protected override string GetMethodDeclaration (MethodDefinition method)
+ {
+ string decl = base.GetMethodDeclaration (method);
+ if (decl != null)
+ return decl + ";";
+ return null;
+ }
+
+ protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
+ {
+ if (DocUtils.IsExplicitlyImplemented (method))
+ {
+ TypeReference iface;
+ MethodReference ifaceMethod;
+ DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
+ return buf.Append (new CSharpMemberFormatter ().GetName (iface))
+ .Append ('.')
+ .Append (ifaceMethod.Name);
+ }
+
+ if (method.Name.StartsWith ("op_", StringComparison.Ordinal))
+ {
+ // this is an operator
+ switch (method.Name)
+ {
+ case "op_Implicit":
+ case "op_Explicit":
+ buf.Length--; // remove the last space, which assumes a member name is coming
+ return buf;
+ case "op_Addition":
+ case "op_UnaryPlus":
+ return buf.Append ("operator +");
+ case "op_Subtraction":
+ case "op_UnaryNegation":
+ return buf.Append ("operator -");
+ case "op_Division":
+ return buf.Append ("operator /");
+ case "op_Multiply":
+ return buf.Append ("operator *");
+ case "op_Modulus":
+ return buf.Append ("operator %");
+ case "op_BitwiseAnd":
+ return buf.Append ("operator &");
+ case "op_BitwiseOr":
+ return buf.Append ("operator |");
+ case "op_ExclusiveOr":
+ return buf.Append ("operator ^");
+ case "op_LeftShift":
+ return buf.Append ("operator <<");
+ case "op_RightShift":
+ return buf.Append ("operator >>");
+ case "op_LogicalNot":
+ return buf.Append ("operator !");
+ case "op_OnesComplement":
+ return buf.Append ("operator ~");
+ case "op_Decrement":
+ return buf.Append ("operator --");
+ case "op_Increment":
+ return buf.Append ("operator ++");
+ case "op_True":
+ return buf.Append ("operator true");
+ case "op_False":
+ return buf.Append ("operator false");
+ case "op_Equality":
+ return buf.Append ("operator ==");
+ case "op_Inequality":
+ return buf.Append ("operator !=");
+ case "op_LessThan":
+ return buf.Append ("operator <");
+ case "op_LessThanOrEqual":
+ return buf.Append ("operator <=");
+ case "op_GreaterThan":
+ return buf.Append ("operator >");
+ case "op_GreaterThanOrEqual":
+ return buf.Append ("operator >=");
+ default:
+ return base.AppendMethodName (buf, method);
+ }
+ }
+ else
+ return base.AppendMethodName (buf, method);
+ }
+
+ protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
+ {
+ if (method.GenericParameters.Count == 0)
+ return buf;
+ return AppendConstraints (buf, method.GenericParameters);
+ }
+
+ protected override string RefTypeModifier
+ {
+ get { return ""; }
+ }
+
+ protected override string GetFinalizerName (MethodDefinition method)
+ {
+ return "~" + method.DeclaringType.Name + " ()";
+ }
+
+ protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
+ {
+ if (method == null)
+ return buf;
+ if (method.IsPublic)
+ return buf.Append ("public");
+ if (method.IsFamily || method.IsFamilyOrAssembly)
+ return buf.Append ("protected");
+ return buf;
+ }
+
+ protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
+ {
+ string modifiers = String.Empty;
+ if (method.IsStatic) modifiers += " static";
+ if (method.IsVirtual && !method.IsAbstract)
+ {
+ if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
+ else modifiers += " override";
+ }
+ TypeDefinition declType = (TypeDefinition)method.DeclaringType;
+ if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
+ if (method.IsFinal) modifiers += " sealed";
+ if (modifiers == " virtual sealed") modifiers = "";
+
+ switch (method.Name)
+ {
+ case "op_Implicit":
+ modifiers += " implicit operator";
+ break;
+ case "op_Explicit":
+ modifiers += " explicit operator";
+ break;
+ }
+
+ return buf.Append (modifiers);
+ }
+
+ protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
+ {
+ if (method.IsGenericMethod ())
+ {
+ IList<GenericParameter> args = method.GenericParameters;
+ if (args.Count > 0)
+ {
+ buf.Append ("<");
+ buf.Append (args[0].Name);
+ for (int i = 1; i < args.Count; ++i)
+ buf.Append (",").Append (args[i].Name);
+ buf.Append (">");
+ }
+ }
+ return buf;
+ }
+
+ protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
+ {
+ return AppendParameters (buf, method, parameters, '(', ')');
+ }
+
+ private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
+ {
+ buf.Append (begin);
+
+ if (parameters.Count > 0)
+ {
+ if (DocUtils.IsExtensionMethod (method))
+ buf.Append ("this ");
+ AppendParameter (buf, parameters[0]);
+ for (int i = 1; i < parameters.Count; ++i)
+ {
+ buf.Append (", ");
+ AppendParameter (buf, parameters[i]);
+ }
+ }
+
+ return buf.Append (end);
+ }
+
+ private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
+ {
+ if (parameter.ParameterType is ByReferenceType)
+ {
+ if (parameter.IsOut)
+ buf.Append ("out ");
+ else
+ buf.Append ("ref ");
+ }
+ if (parameter.HasCustomAttributes)
+ {
+ var isParams = parameter.CustomAttributes.Any (ca => ca.AttributeType.Name == "ParamArrayAttribute");
+ if (isParams)
+ buf.AppendFormat ("params ");
+ }
+ buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
+ buf.Append (parameter.Name);
+ if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant)
+ {
+ buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
+ }
+ return buf;
+ }
+
+ protected override string GetPropertyDeclaration (PropertyDefinition property)
+ {
+ MethodDefinition method;
+
+ string get_visible = null;
+ if ((method = property.GetMethod) != null &&
+ (DocUtils.IsExplicitlyImplemented (method) ||
+ (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
+ get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
+ string set_visible = null;
+ if ((method = property.SetMethod) != null &&
+ (DocUtils.IsExplicitlyImplemented (method) ||
+ (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
+ set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
+
+ if ((set_visible == null) && (get_visible == null))
+ return null;
+
+ string visibility;
+ StringBuilder buf = new StringBuilder ();
+ if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
+ buf.Append (visibility = get_visible);
+ else if (set_visible != null && get_visible == null)
+ buf.Append (visibility = set_visible);
+ else
+ buf.Append (visibility = "public");
+
+ // Pick an accessor to use for static/virtual/override/etc. checks.
+ method = property.SetMethod;
+ if (method == null)
+ method = property.GetMethod;
+
+ string modifiers = String.Empty;
+ if (method.IsStatic) modifiers += " static";
+ if (method.IsVirtual && !method.IsAbstract)
+ {
+ if ((method.Attributes & MethodAttributes.NewSlot) != 0)
+ modifiers += " virtual";
+ else
+ modifiers += " override";
+ }
+ TypeDefinition declDef = (TypeDefinition)method.DeclaringType;
+ if (method.IsAbstract && !declDef.IsInterface)
+ modifiers += " abstract";
+ if (method.IsFinal)
+ modifiers += " sealed";
+ if (modifiers == " virtual sealed")
+ modifiers = "";
+ buf.Append (modifiers).Append (' ');
+
+ buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
+
+ IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
+ string name = property.Name;
+ foreach (MemberReference mi in defs)
+ {
+ if (mi == property)
+ {
+ name = "this";
+ break;
+ }
+ }
+ buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
+
+ if (property.Parameters.Count != 0)
+ {
+ AppendParameters (buf, method, property.Parameters, '[', ']');
+ }
+
+ buf.Append (" {");
+ if (get_visible != null)
+ {
+ if (get_visible != visibility)
+ buf.Append (' ').Append (get_visible);
+ buf.Append (" get;");
+ }
+ if (set_visible != null)
+ {
+ if (set_visible != visibility)
+ buf.Append (' ').Append (set_visible);
+ buf.Append (" set;");
+ }
+ buf.Append (" }");
+
+ return buf[0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length - 1);
+ }
+
+ protected override string GetFieldDeclaration (FieldDefinition field)
+ {
+ TypeDefinition declType = (TypeDefinition)field.DeclaringType;
+ if (declType.IsEnum && field.Name == "value__")
+ return null; // This member of enums aren't documented.
+
+ StringBuilder buf = new StringBuilder ();
+ AppendFieldVisibility (buf, field);
+ if (buf.Length == 0)
+ return null;
+
+ if (declType.IsEnum)
+ return field.Name;
+
+ if (field.IsStatic && !field.IsLiteral)
+ buf.Append (" static");
+ if (field.IsInitOnly)
+ buf.Append (" readonly");
+ if (field.IsLiteral)
+ buf.Append (" const");
+
+ buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
+ buf.Append (field.Name);
+ AppendFieldValue (buf, field);
+ buf.Append (';');
+
+ return buf.ToString ();
+ }
+
+ static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
+ {
+ if (field.IsPublic)
+ return buf.Append ("public");
+ if (field.IsFamily || field.IsFamilyOrAssembly)
+ return buf.Append ("protected");
+ return buf;
+ }
+
+ static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
+ {
+ // enums have a value__ field, which we ignore
+ if (((TypeDefinition)field.DeclaringType).IsEnum ||
+ field.DeclaringType.IsGenericType ())
+ return buf;
+ if (field.HasConstant && field.IsLiteral)
+ {
+ object val = null;
+ try
+ {
+ val = field.Constant;
+ }
+ catch
+ {
+ return buf;
+ }
+ if (val == null)
+ buf.Append (" = ").Append ("null");
+ else if (val is Enum)
+ buf.Append (" = ").Append (val.ToString ());
+ else if (val is IFormattable)
+ {
+ string value = ((IFormattable)val).ToString (null, CultureInfo.InvariantCulture);
+ if (val is string)
+ value = "\"" + value + "\"";
+ buf.Append (" = ").Append (value);
+ }
+ }
+ return buf;
+ }
+
+ protected override string GetEventDeclaration (EventDefinition e)
+ {
+ StringBuilder buf = new StringBuilder ();
+ if (AppendVisibility (buf, e.AddMethod).Length == 0)
+ {
+ return null;
+ }
+
+ AppendModifiers (buf, e.AddMethod);
+
+ buf.Append (" event ");
+ buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters[0]))).Append (' ');
+ buf.Append (e.Name).Append (';');
+
+ return buf.ToString ();
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/Formatters/CSharpMemberFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/CSharpMemberFormatter.cs
new file mode 100644
index 00000000..56129c7b
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/Formatters/CSharpMemberFormatter.cs
@@ -0,0 +1,14 @@
+using System.Text;
+
+using Mono.Cecil;
+
+namespace Mono.Documentation.Updater
+{
+ public class CSharpMemberFormatter : CSharpFullMemberFormatter
+ {
+ protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
+ {
+ return buf;
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/Formatters/CSharpNativeTypeMemberFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/CSharpNativeTypeMemberFormatter.cs
new file mode 100644
index 00000000..9cd2ee9b
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/Formatters/CSharpNativeTypeMemberFormatter.cs
@@ -0,0 +1,26 @@
+namespace Mono.Documentation.Updater
+{
+ class CSharpNativeTypeMemberFormatter : CSharpFullMemberFormatter
+ {
+ protected override string GetCSharpType (string t)
+ {
+ string moddedType = base.GetCSharpType (t);
+
+ switch (moddedType)
+ {
+ case "int": return "nint";
+ case "uint":
+ return "nuint";
+ case "float":
+ return "nfloat";
+ case "System.Drawing.SizeF":
+ return "CoreGraphics.CGSize";
+ case "System.Drawing.PointF":
+ return "CoreGraphics.CGPoint";
+ case "System.Drawing.RectangleF":
+ return "CoreGraphics.CGPoint";
+ }
+ return null;
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/Formatters/DefaultAttributeValueFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/DefaultAttributeValueFormatter.cs
new file mode 100644
index 00000000..dac130d2
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/Formatters/DefaultAttributeValueFormatter.cs
@@ -0,0 +1,13 @@
+namespace Mono.Documentation.Updater
+{
+ /// <summary>The final value formatter in the pipeline ... if no other formatter formats the value,
+ /// then this one will serve as the default implementation.</summary>
+ class DefaultAttributeValueFormatter : AttributeValueFormatter
+ {
+ public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
+ {
+ returnvalue = "(" + MDocUpdater.GetDocTypeFullName (type.Reference) + ") " + v.ToString ();
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/Formatters/DocIdFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/DocIdFormatter.cs
new file mode 100644
index 00000000..7169ab93
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/Formatters/DocIdFormatter.cs
@@ -0,0 +1,23 @@
+
+using Mono.Cecil;
+using Mono.Cecil.Rocks;
+
+namespace Mono.Documentation.Updater
+{
+ class DocIdFormatter : MemberFormatter
+ {
+ public override string Language
+ {
+ get { return "DocId"; }
+ }
+
+ public override string GetDeclaration (TypeReference tref)
+ {
+ return DocCommentId.GetDocCommentId (tref.Resolve ());
+ }
+ public override string GetDeclaration (MemberReference mreference)
+ {
+ return DocCommentId.GetDocCommentId (mreference.Resolve ());
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/Formatters/DocTypeFullMemberFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/DocTypeFullMemberFormatter.cs
new file mode 100644
index 00000000..087b9bb8
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/Formatters/DocTypeFullMemberFormatter.cs
@@ -0,0 +1,12 @@
+namespace Mono.Documentation.Updater
+{
+ class DocTypeFullMemberFormatter : MemberFormatter
+ {
+ public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
+
+ protected override char NestedTypeSeparator
+ {
+ get { return '+'; }
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/Formatters/DocTypeMemberFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/DocTypeMemberFormatter.cs
new file mode 100644
index 00000000..d87a0604
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/Formatters/DocTypeMemberFormatter.cs
@@ -0,0 +1,14 @@
+using System.Text;
+
+using Mono.Cecil;
+
+namespace Mono.Documentation.Updater
+{
+ class DocTypeMemberFormatter : DocTypeFullMemberFormatter
+ {
+ protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
+ {
+ return buf;
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/Formatters/FileNameMemberFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/FileNameMemberFormatter.cs
new file mode 100644
index 00000000..8fb7a54b
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/Formatters/FileNameMemberFormatter.cs
@@ -0,0 +1,19 @@
+using System.Text;
+
+using Mono.Cecil;
+
+namespace Mono.Documentation.Updater
+{
+ class FileNameMemberFormatter : SlashDocMemberFormatter
+ {
+ protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
+ {
+ return buf;
+ }
+
+ protected override char NestedTypeSeparator
+ {
+ get { return '+'; }
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/Formatters/ILFullMemberFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/ILFullMemberFormatter.cs
new file mode 100644
index 00000000..bd21c204
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/Formatters/ILFullMemberFormatter.cs
@@ -0,0 +1,573 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+
+using Mono.Cecil;
+
+using Mono.Documentation.Util;
+
+namespace Mono.Documentation.Updater
+{
+ public class ILFullMemberFormatter : MemberFormatter
+ {
+
+ public override string Language
+ {
+ get { return "ILAsm"; }
+ }
+
+ protected override char NestedTypeSeparator
+ {
+ get
+ {
+ return '/';
+ }
+ }
+
+ protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
+ {
+ if (GetBuiltinType (type.FullName) != null)
+ return buf;
+ string ns = DocUtils.GetNamespace (type);
+ if (ns != null && ns.Length > 0)
+ {
+ if (type.IsValueType)
+ buf.Append ("valuetype ");
+ else
+ buf.Append ("class ");
+ buf.Append (ns).Append ('.');
+ }
+ return buf;
+ }
+
+ protected static string GetBuiltinType (string t)
+ {
+ switch (t)
+ {
+ case "System.Byte": return "unsigned int8";
+ case "System.SByte": return "int8";
+ case "System.Int16": return "int16";
+ case "System.Int32": return "int32";
+ case "System.Int64": return "int64";
+ case "System.IntPtr": return "native int";
+
+ case "System.UInt16": return "unsigned int16";
+ case "System.UInt32": return "unsigned int32";
+ case "System.UInt64": return "unsigned int64";
+ case "System.UIntPtr": return "native unsigned int";
+
+ case "System.Single": return "float32";
+ case "System.Double": return "float64";
+ case "System.Boolean": return "bool";
+ case "System.Char": return "char";
+ case "System.Void": return "void";
+ case "System.String": return "string";
+ case "System.Object": return "object";
+ }
+ return null;
+ }
+
+ protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
+ {
+ return buf.Append (typename);
+ }
+
+ protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
+ {
+ if (type is GenericParameter)
+ {
+ AppendGenericParameterConstraints (buf, (GenericParameter)type).Append (type.Name);
+ return buf;
+ }
+
+ string s = GetBuiltinType (type.FullName);
+ if (s != null)
+ {
+ return buf.Append (s);
+ }
+ return base.AppendTypeName (buf, type, context);
+ }
+
+ private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
+ {
+ if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
+ {
+ return buf.Append (type.Owner is TypeReference ? "!" : "!!");
+ }
+ GenericParameterAttributes attrs = type.Attributes;
+ if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
+ buf.Append ("class ");
+ if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
+ buf.Append ("struct ");
+ if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
+ buf.Append (".ctor ");
+ IList<TypeReference> constraints = type.Constraints;
+ MemberFormatterState = 0;
+ if (constraints.Count > 0)
+ {
+ var full = new ILFullMemberFormatter ();
+ buf.Append ("(").Append (full.GetName (constraints[0]));
+ for (int i = 1; i < constraints.Count; ++i)
+ {
+ buf.Append (", ").Append (full.GetName (constraints[i]));
+ }
+ buf.Append (") ");
+ }
+ MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
+
+ if ((attrs & GenericParameterAttributes.Covariant) != 0)
+ buf.Append ("+ ");
+ if ((attrs & GenericParameterAttributes.Contravariant) != 0)
+ buf.Append ("- ");
+ return buf;
+ }
+
+ protected override string GetTypeDeclaration (TypeDefinition type)
+ {
+ string visibility = GetTypeVisibility (type.Attributes);
+ if (visibility == null)
+ return null;
+
+ StringBuilder buf = new StringBuilder ();
+
+ buf.Append (".class ");
+ if (type.IsNested)
+ buf.Append ("nested ");
+ buf.Append (visibility).Append (" ");
+ if (type.IsInterface)
+ buf.Append ("interface ");
+ if (type.IsSequentialLayout)
+ buf.Append ("sequential ");
+ if (type.IsAutoLayout)
+ buf.Append ("auto ");
+ if (type.IsAnsiClass)
+ buf.Append ("ansi ");
+ if (type.IsAbstract)
+ buf.Append ("abstract ");
+ if (type.IsSerializable)
+ buf.Append ("serializable ");
+ if (type.IsSealed)
+ buf.Append ("sealed ");
+ if (type.IsBeforeFieldInit)
+ buf.Append ("beforefieldinit ");
+ var state = MemberFormatterState;
+ MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
+ buf.Append (GetName (type));
+ MemberFormatterState = state;
+ var full = new ILFullMemberFormatter ();
+ if (type.BaseType != null)
+ {
+ buf.Append (" extends ");
+ if (type.BaseType.FullName == "System.Object")
+ buf.Append ("System.Object");
+ else
+ buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
+ }
+ bool first = true;
+ foreach (var name in type.Interfaces.Where (i => MDocUpdater.IsPublic (i.InterfaceType.Resolve ()))
+ .Select (i => full.GetName (i.InterfaceType))
+ .OrderBy (n => n))
+ {
+ if (first)
+ {
+ buf.Append (" implements ");
+ first = false;
+ }
+ else
+ {
+ buf.Append (", ");
+ }
+ buf.Append (name);
+ }
+
+ return buf.ToString ();
+ }
+
+ protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
+ {
+ List<TypeReference> decls = DocUtils.GetDeclaringTypes (
+ type is GenericInstanceType ? type.GetElementType () : type);
+ bool first = true;
+ foreach (var decl in decls)
+ {
+ TypeReference declDef = decl.Resolve () ?? decl;
+ if (!first)
+ {
+ buf.Append (NestedTypeSeparator);
+ }
+ first = false;
+ AppendTypeName (buf, declDef, context);
+ }
+ buf.Append ('<');
+ first = true;
+ foreach (TypeReference arg in GetGenericArguments (type))
+ {
+ if (!first)
+ buf.Append (", ");
+ first = false;
+ _AppendTypeName (buf, arg, context);
+ }
+ buf.Append ('>');
+ return buf;
+ }
+
+ static string GetTypeVisibility (TypeAttributes ta)
+ {
+ switch (ta & TypeAttributes.VisibilityMask)
+ {
+ case TypeAttributes.Public:
+ case TypeAttributes.NestedPublic:
+ return "public";
+
+ case TypeAttributes.NestedFamily:
+ case TypeAttributes.NestedFamORAssem:
+ return "protected";
+
+ default:
+ return null;
+ }
+ }
+
+ protected override string GetConstructorDeclaration (MethodDefinition constructor)
+ {
+ return GetMethodDeclaration (constructor);
+ }
+
+ protected override string GetMethodDeclaration (MethodDefinition method)
+ {
+ if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
+ return null;
+
+ var buf = new StringBuilder ();
+ buf.Append (".method ");
+ AppendVisibility (buf, method);
+ if (method.IsStatic)
+ buf.Append ("static ");
+ if (method.IsHideBySig)
+ buf.Append ("hidebysig ");
+ if (method.IsPInvokeImpl)
+ {
+ var info = method.PInvokeInfo;
+ buf.Append ("pinvokeimpl (\"")
+ .Append (info.Module.Name)
+ .Append ("\" as \"")
+ .Append (info.EntryPoint)
+ .Append ("\"");
+ if (info.IsCharSetAuto)
+ buf.Append (" auto");
+ if (info.IsCharSetUnicode)
+ buf.Append (" unicode");
+ if (info.IsCharSetAnsi)
+ buf.Append (" ansi");
+ if (info.IsCallConvCdecl)
+ buf.Append (" cdecl");
+ if (info.IsCallConvStdCall)
+ buf.Append (" stdcall");
+ if (info.IsCallConvWinapi)
+ buf.Append (" winapi");
+ if (info.IsCallConvThiscall)
+ buf.Append (" thiscall");
+ if (info.SupportsLastError)
+ buf.Append (" lasterr");
+ buf.Append (")");
+ }
+ if (method.IsSpecialName)
+ buf.Append ("specialname ");
+ if (method.IsRuntimeSpecialName)
+ buf.Append ("rtspecialname ");
+ if (method.IsNewSlot)
+ buf.Append ("newslot ");
+ if (method.IsVirtual)
+ buf.Append ("virtual ");
+ if (!method.IsStatic)
+ buf.Append ("instance ");
+ _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
+ buf.Append (' ')
+ .Append (method.Name);
+ if (method.IsGenericMethod ())
+ {
+ var state = MemberFormatterState;
+ MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
+ IList<GenericParameter> args = method.GenericParameters;
+ if (args.Count > 0)
+ {
+ buf.Append ("<");
+ _AppendTypeName (buf, args[0], null);
+ for (int i = 1; i < args.Count; ++i)
+ _AppendTypeName (buf.Append (", "), args[i], null);
+ buf.Append (">");
+ }
+ MemberFormatterState = state;
+ }
+
+ buf.Append ('(');
+ bool first = true;
+ for (int i = 0; i < method.Parameters.Count; ++i)
+ {
+ var param = method.Parameters[i];
+ if (!first)
+ buf.Append (", ");
+ first = false;
+
+ if (param.IsOut) buf.Append ("[out] ");
+ else if (param.IsIn) buf.Append ("[in]");
+
+ _AppendTypeName (buf, param.ParameterType, new DynamicParserContext (param));
+ if (param.ParameterType.IsByReference) buf.Append ("&");
+ buf.Append (' ');
+ buf.Append (param.Name);
+ }
+ buf.Append (')');
+ if (method.IsIL)
+ buf.Append (" cil");
+ if (method.IsRuntime)
+ buf.Append (" runtime");
+ if (method.IsManaged)
+ buf.Append (" managed");
+
+ return buf.ToString ();
+ }
+
+ protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
+ {
+ if (DocUtils.IsExplicitlyImplemented (method))
+ {
+ TypeReference iface;
+ MethodReference ifaceMethod;
+ DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
+ return buf.Append (new CSharpMemberFormatter ().GetName (iface))
+ .Append ('.')
+ .Append (ifaceMethod.Name);
+ }
+ return base.AppendMethodName (buf, method);
+ }
+
+ protected override string RefTypeModifier
+ {
+ get { return ""; }
+ }
+
+ protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
+ {
+ if (method.IsPublic)
+ return buf.Append ("public ");
+ if (method.IsFamilyAndAssembly)
+ return buf.Append ("familyandassembly");
+ if (method.IsFamilyOrAssembly)
+ return buf.Append ("familyorassembly");
+ if (method.IsFamily)
+ return buf.Append ("family");
+ return buf;
+ }
+
+ protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
+ {
+ string modifiers = String.Empty;
+ if (method.IsStatic) modifiers += " static";
+ if (method.IsVirtual && !method.IsAbstract)
+ {
+ if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
+ else modifiers += " override";
+ }
+ TypeDefinition declType = (TypeDefinition)method.DeclaringType;
+ if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
+ if (method.IsFinal) modifiers += " sealed";
+ if (modifiers == " virtual sealed") modifiers = "";
+
+ return buf.Append (modifiers);
+ }
+
+ protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
+ {
+ if (method.IsGenericMethod ())
+ {
+ IList<GenericParameter> args = method.GenericParameters;
+ if (args.Count > 0)
+ {
+ buf.Append ("<");
+ buf.Append (args[0].Name);
+ for (int i = 1; i < args.Count; ++i)
+ buf.Append (",").Append (args[i].Name);
+ buf.Append (">");
+ }
+ }
+ return buf;
+ }
+
+ protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
+ {
+ return AppendParameters (buf, method, parameters, '(', ')');
+ }
+
+ private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
+ {
+ buf.Append (begin);
+
+ if (parameters.Count > 0)
+ {
+ if (DocUtils.IsExtensionMethod (method))
+ buf.Append ("this ");
+ AppendParameter (buf, parameters[0]);
+ for (int i = 1; i < parameters.Count; ++i)
+ {
+ buf.Append (", ");
+ AppendParameter (buf, parameters[i]);
+ }
+ }
+
+ return buf.Append (end);
+ }
+
+ private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
+ {
+ if (parameter.ParameterType is ByReferenceType)
+ {
+ if (parameter.IsOut)
+ buf.Append ("out ");
+ else
+ buf.Append ("ref ");
+ }
+ buf.Append (GetName (parameter.ParameterType)).Append (" ");
+ return buf.Append (parameter.Name);
+ }
+
+ protected override string GetPropertyDeclaration (PropertyDefinition property)
+ {
+ MethodDefinition gm = null, sm = null;
+
+ string get_visible = null;
+ if ((gm = property.GetMethod) != null &&
+ (DocUtils.IsExplicitlyImplemented (gm) ||
+ (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
+ get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
+ string set_visible = null;
+ if ((sm = property.SetMethod) != null &&
+ (DocUtils.IsExplicitlyImplemented (sm) ||
+ (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
+ set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
+
+ if ((set_visible == null) && (get_visible == null))
+ return null;
+
+ StringBuilder buf = new StringBuilder ()
+ .Append (".property ");
+ if (!(gm ?? sm).IsStatic)
+ buf.Append ("instance ");
+ _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
+ buf.Append (' ').Append (property.Name);
+ if (!property.HasParameters || property.Parameters.Count == 0)
+ return buf.ToString ();
+
+ buf.Append ('(');
+ bool first = true;
+ foreach (ParameterDefinition p in property.Parameters)
+ {
+ if (!first)
+ buf.Append (", ");
+ first = false;
+ _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
+ }
+ buf.Append (')');
+
+ return buf.ToString ();
+ }
+
+ protected override string GetFieldDeclaration (FieldDefinition field)
+ {
+ TypeDefinition declType = (TypeDefinition)field.DeclaringType;
+ if (declType.IsEnum && field.Name == "value__")
+ return null; // This member of enums aren't documented.
+
+ StringBuilder buf = new StringBuilder ();
+ AppendFieldVisibility (buf, field);
+ if (buf.Length == 0)
+ return null;
+
+ buf.Insert (0, ".field ");
+
+ if (field.IsStatic)
+ buf.Append ("static ");
+ if (field.IsInitOnly)
+ buf.Append ("initonly ");
+ if (field.IsLiteral)
+ buf.Append ("literal ");
+ _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
+ buf.Append (' ').Append (field.Name);
+ AppendFieldValue (buf, field);
+
+ return buf.ToString ();
+ }
+
+ static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
+ {
+ if (field.IsPublic)
+ return buf.Append ("public ");
+ if (field.IsFamilyAndAssembly)
+ return buf.Append ("familyandassembly ");
+ if (field.IsFamilyOrAssembly)
+ return buf.Append ("familyorassembly ");
+ if (field.IsFamily)
+ return buf.Append ("family ");
+ return buf;
+ }
+
+ static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
+ {
+ // enums have a value__ field, which we ignore
+ if (field.DeclaringType.IsGenericType ())
+ return buf;
+ if (field.HasConstant && field.IsLiteral)
+ {
+ object val = null;
+ try
+ {
+ val = field.Constant;
+ }
+ catch
+ {
+ return buf;
+ }
+ if (val == null)
+ buf.Append (" = ").Append ("null");
+ else if (val is Enum)
+ buf.Append (" = ")
+ .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
+ .Append ('(')
+ .Append (val.ToString ())
+ .Append (')');
+ else if (val is IFormattable)
+ {
+ string value = ((IFormattable)val).ToString (null, CultureInfo.InvariantCulture);
+ buf.Append (" = ");
+ if (val is string)
+ buf.Append ("\"" + value + "\"");
+ else
+ buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
+ .Append ('(')
+ .Append (value)
+ .Append (')');
+ }
+ }
+ return buf;
+ }
+
+ protected override string GetEventDeclaration (EventDefinition e)
+ {
+ StringBuilder buf = new StringBuilder ();
+ if (AppendVisibility (buf, e.AddMethod).Length == 0)
+ {
+ return null;
+ }
+
+ buf.Length = 0;
+ buf.Append (".event ")
+ .Append (GetName (e.EventType))
+ .Append (' ')
+ .Append (e.Name);
+
+ return buf.ToString ();
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/Formatters/ILMemberFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/ILMemberFormatter.cs
new file mode 100644
index 00000000..b520f9e5
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/Formatters/ILMemberFormatter.cs
@@ -0,0 +1,14 @@
+using System.Text;
+
+using Mono.Cecil;
+
+namespace Mono.Documentation.Updater
+{
+ public class ILMemberFormatter : ILFullMemberFormatter
+ {
+ protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
+ {
+ return buf;
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/Formatters/ILNativeTypeMemberFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/ILNativeTypeMemberFormatter.cs
new file mode 100644
index 00000000..70795c3b
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/Formatters/ILNativeTypeMemberFormatter.cs
@@ -0,0 +1,10 @@
+namespace Mono.Documentation.Updater
+{
+ class ILNativeTypeMemberFormatter : ILFullMemberFormatter
+ {
+ protected static string _GetBuiltinType (string t)
+ {
+ return null;
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/Formatters/MemberFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/MemberFormatter.cs
new file mode 100644
index 00000000..98cd0d4d
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/Formatters/MemberFormatter.cs
@@ -0,0 +1,421 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using Mono.Cecil;
+
+using Mono.Documentation.Util;
+
+namespace Mono.Documentation.Updater
+{
+ public abstract class MemberFormatter
+ {
+
+ public virtual string Language
+ {
+ get { return ""; }
+ }
+
+ public string GetName (MemberReference member)
+ {
+ return GetName (member, null);
+ }
+
+ public virtual string GetName (MemberReference member, DynamicParserContext context)
+ {
+ TypeReference type = member as TypeReference;
+ if (type != null)
+ return GetTypeName (type, context);
+ MethodReference method = member as MethodReference;
+ if (method != null && method.Name == ".ctor") // method.IsConstructor
+ return GetConstructorName (method);
+ if (method != null)
+ return GetMethodName (method);
+ PropertyReference prop = member as PropertyReference;
+ if (prop != null)
+ return GetPropertyName (prop);
+ FieldReference field = member as FieldReference;
+ if (field != null)
+ return GetFieldName (field);
+ EventReference e = member as EventReference;
+ if (e != null)
+ return GetEventName (e);
+ throw new NotSupportedException ("Can't handle: " +
+ (member == null ? "null" : member.GetType ().ToString ()));
+ }
+
+ protected virtual string GetTypeName (TypeReference type)
+ {
+ return GetTypeName (type, null);
+ }
+
+ protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
+ {
+ if (type == null)
+ throw new ArgumentNullException (nameof (type));
+
+ var typeName = _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
+
+ typeName = RemoveMod (typeName);
+
+ return typeName;
+ }
+
+ public static string RemoveMod (string typeName)
+ {
+ if (string.IsNullOrWhiteSpace (typeName)) return typeName;
+
+ // For custom modifiers (modopt/modreq), we are just excising them
+ // via string manipulation for simplicity; since these cannot be
+ // expressed in C#. If finer control is needed in the future, you can
+ // use IModifierType, PointerType, ByReferenceType, etc.
+
+ int modIndex = Math.Max (typeName.LastIndexOf ("modopt(", StringComparison.Ordinal), typeName.LastIndexOf ("modreq(", StringComparison.Ordinal));
+ if (modIndex > 0)
+ {
+ var tname = typeName.Substring (0, modIndex - 1);
+ var parenIndex = typeName.LastIndexOf (')');
+ if (parenIndex == typeName.Length - 2)
+ { // see if there's metadata like a pointer
+ tname += typeName.Last ();
+ }
+ typeName = tname;
+ }
+
+ modIndex = Math.Max (typeName.LastIndexOf ("modopt(", StringComparison.Ordinal), typeName.LastIndexOf ("modreq(", StringComparison.Ordinal));
+ if (modIndex >= 0)
+ return RemoveMod (typeName);
+ else
+ return typeName;
+ }
+
+ protected virtual char[] ArrayDelimeters
+ {
+ get { return new char[] { '[', ']' }; }
+ }
+
+ protected virtual MemberFormatterState MemberFormatterState { get; set; }
+
+ protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
+ {
+ if (type is ArrayType)
+ {
+ TypeSpecification spec = type as TypeSpecification;
+ _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
+ return AppendArrayModifiers (buf, (ArrayType)type);
+ }
+ if (type is ByReferenceType)
+ {
+ return AppendRefTypeName (buf, type, context);
+ }
+ if (type is PointerType)
+ {
+ return AppendPointerTypeName (buf, type, context);
+ }
+ if (type is GenericParameter)
+ {
+ return AppendTypeName (buf, type, context);
+ }
+ AppendNamespace (buf, type);
+ GenericInstanceType genInst = type as GenericInstanceType;
+ if (type.GenericParameters.Count == 0 &&
+ (genInst == null ? true : genInst.GenericArguments.Count == 0))
+ {
+ return AppendFullTypeName (buf, type, context);
+ }
+ return AppendGenericType (buf, type, context);
+ }
+
+ protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
+ {
+ string ns = DocUtils.GetNamespace (type);
+ if (ns != null && ns.Length > 0)
+ buf.Append (ns).Append ('.');
+ return buf;
+ }
+
+ protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
+ {
+ if (type.DeclaringType != null)
+ AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
+ return AppendTypeName (buf, type, context);
+ }
+
+ protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
+ {
+ if (context != null)
+ context.TransformIndex++;
+ return AppendTypeName (buf, type.Name);
+ }
+
+ protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
+ {
+ int n = typename.IndexOf ("`");
+ if (n >= 0)
+ return buf.Append (typename.Substring (0, n));
+ return buf.Append (typename);
+ }
+
+ protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
+ {
+ buf.Append (ArrayDelimeters[0]);
+ int rank = array.Rank;
+ if (rank > 1)
+ buf.Append (new string (',', rank - 1));
+ return buf.Append (ArrayDelimeters[1]);
+ }
+
+ protected virtual string RefTypeModifier
+ {
+ get { return "@"; }
+ }
+
+ protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
+ {
+ TypeSpecification spec = type as TypeSpecification;
+ return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
+ .Append (RefTypeModifier);
+ }
+
+ protected virtual string PointerModifier
+ {
+ get { return "*"; }
+ }
+
+ protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
+ {
+ TypeSpecification spec = type as TypeSpecification;
+ return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
+ .Append (PointerModifier);
+ }
+
+ protected virtual char[] GenericTypeContainer
+ {
+ get { return new char[] { '<', '>' }; }
+ }
+
+ protected virtual char NestedTypeSeparator
+ {
+ get { return '.'; }
+ }
+
+ protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
+ {
+ List<TypeReference> decls = DocUtils.GetDeclaringTypes (
+ type is GenericInstanceType ? type.GetElementType () : type);
+ List<TypeReference> genArgs = GetGenericArguments (type);
+ int argIdx = 0;
+ int prev = 0;
+ bool insertNested = false;
+ foreach (var decl in decls)
+ {
+ TypeReference declDef = decl.Resolve () ?? decl;
+ if (insertNested)
+ {
+ buf.Append (NestedTypeSeparator);
+ }
+ insertNested = true;
+ AppendTypeName (buf, declDef, context);
+ int ac = DocUtils.GetGenericArgumentCount (declDef);
+ int c = ac - prev;
+ prev = ac;
+ if (c > 0)
+ {
+ buf.Append (GenericTypeContainer[0]);
+ var origState = MemberFormatterState;
+ MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
+ _AppendTypeName (buf, genArgs[argIdx++], context);
+ for (int i = 1; i < c; ++i)
+ {
+ _AppendTypeName (buf.Append (","), genArgs[argIdx++], context);
+ }
+ MemberFormatterState = origState;
+ buf.Append (GenericTypeContainer[1]);
+ }
+ }
+ return buf;
+ }
+
+ protected List<TypeReference> GetGenericArguments (TypeReference type)
+ {
+ var args = new List<TypeReference> ();
+ GenericInstanceType inst = type as GenericInstanceType;
+ if (inst != null)
+ args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
+ else
+ args.AddRange (type.GenericParameters.Cast<TypeReference> ());
+ return args;
+ }
+
+ protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
+ {
+ return buf;
+ }
+
+ protected virtual string GetConstructorName (MethodReference constructor)
+ {
+ return constructor.Name;
+ }
+
+ protected virtual string GetMethodName (MethodReference method)
+ {
+ return method.Name;
+ }
+
+ protected virtual string GetPropertyName (PropertyReference property)
+ {
+ return property.Name;
+ }
+
+ protected virtual string GetFieldName (FieldReference field)
+ {
+ return field.Name;
+ }
+
+ protected virtual string GetEventName (EventReference e)
+ {
+ return e.Name;
+ }
+
+ public virtual string GetDeclaration (TypeReference tref)
+ {
+ var typeSpec = tref as TypeSpecification;
+ if (typeSpec != null && typeSpec.Resolve () == null && typeSpec.IsArray && typeSpec.ContainsGenericParameter)
+ {
+ //HACK: there's really no good reference for a generic parameter array, so we're going to use object
+ return "T:System.Array";
+ }
+ TypeDefinition def = tref.Resolve ();
+ if (def != null)
+ return GetTypeDeclaration (def);
+ else
+ return GetTypeName (tref);
+ }
+
+ public virtual string GetDeclaration (MemberReference mreference)
+ {
+ return GetDeclaration (mreference.Resolve ());
+ }
+
+ string GetDeclaration (IMemberDefinition member)
+ {
+ if (member == null)
+ throw new ArgumentNullException ("member");
+ TypeDefinition type = member as TypeDefinition;
+ if (type != null)
+ return GetTypeDeclaration (type);
+ MethodDefinition method = member as MethodDefinition;
+ if (method != null && method.IsConstructor)
+ return GetConstructorDeclaration (method);
+ if (method != null)
+ return GetMethodDeclaration (method);
+ PropertyDefinition prop = member as PropertyDefinition;
+ if (prop != null)
+ return GetPropertyDeclaration (prop);
+ FieldDefinition field = member as FieldDefinition;
+ if (field != null)
+ return GetFieldDeclaration (field);
+ EventDefinition e = member as EventDefinition;
+ if (e != null)
+ return GetEventDeclaration (e);
+ throw new NotSupportedException ("Can't handle: " + member.GetType ().ToString ());
+ }
+
+ protected virtual string GetTypeDeclaration (TypeDefinition type)
+ {
+ if (type == null)
+ throw new ArgumentNullException ("type");
+ StringBuilder buf = new StringBuilder (type.Name.Length);
+ _AppendTypeName (buf, type, null);
+ AppendGenericTypeConstraints (buf, type);
+ return buf.ToString ();
+ }
+
+ protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
+ {
+ return GetConstructorName (constructor);
+ }
+
+ protected virtual string GetMethodDeclaration (MethodDefinition method)
+ {
+ if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute> ().Any (
+ ca => ca.GetDeclaringType () == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
+ return null;
+
+ // Special signature for destructors.
+ if (method.Name == "Finalize" && method.Parameters.Count == 0)
+ return GetFinalizerName (method);
+
+ StringBuilder buf = new StringBuilder ();
+
+ AppendVisibility (buf, method);
+ if (buf.Length == 0 &&
+ !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
+ return null;
+
+ AppendModifiers (buf, method);
+
+ if (buf.Length != 0)
+ buf.Append (" ");
+
+ buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
+
+ AppendMethodName (buf, method);
+ AppendGenericMethod (buf, method).Append (" ");
+ AppendParameters (buf, method, method.Parameters);
+ AppendGenericMethodConstraints (buf, method);
+ return buf.ToString ();
+ }
+
+ protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
+ {
+ return buf.Append (method.Name);
+ }
+
+ protected virtual string GetFinalizerName (MethodDefinition method)
+ {
+ return "Finalize";
+ }
+
+ protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
+ {
+ return buf;
+ }
+
+ protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
+ {
+ return buf;
+ }
+
+ protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
+ {
+ return buf;
+ }
+
+ protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
+ {
+ return buf;
+ }
+
+ protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
+ {
+ return buf;
+ }
+
+ protected virtual string GetPropertyDeclaration (PropertyDefinition property)
+ {
+ return GetPropertyName (property);
+ }
+
+ protected virtual string GetFieldDeclaration (FieldDefinition field)
+ {
+ return GetFieldName (field);
+ }
+
+ protected virtual string GetEventDeclaration (EventDefinition e)
+ {
+ return GetEventName (e);
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/Formatters/SlashDocMemberFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/SlashDocMemberFormatter.cs
new file mode 100644
index 00000000..05a7c4df
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/Formatters/SlashDocMemberFormatter.cs
@@ -0,0 +1,321 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using Mono.Cecil;
+
+using Mono.Documentation.Util;
+
+namespace Mono.Documentation.Updater
+{
+ class SlashDocMemberFormatter : MemberFormatter
+ {
+
+ protected override char[] GenericTypeContainer
+ {
+ get { return new char[] { '{', '}' }; }
+ }
+
+ private bool AddTypeCount = true;
+
+ private TypeReference genDeclType;
+ private MethodReference genDeclMethod;
+
+ protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
+ {
+ if (type is GenericParameter)
+ {
+ int l = buf.Length;
+ if (genDeclType != null)
+ {
+ IList<GenericParameter> genArgs = genDeclType.GenericParameters;
+ for (int i = 0; i < genArgs.Count; ++i)
+ {
+ if (genArgs[i].Name == type.Name)
+ {
+ buf.Append ('`').Append (i);
+ break;
+ }
+ }
+ }
+ if (genDeclMethod != null)
+ {
+ IList<GenericParameter> genArgs = null;
+ if (genDeclMethod.IsGenericMethod ())
+ {
+ genArgs = genDeclMethod.GenericParameters;
+ for (int i = 0; i < genArgs.Count; ++i)
+ {
+ if (genArgs[i].Name == type.Name)
+ {
+ buf.Append ("``").Append (i);
+ break;
+ }
+ }
+ }
+ }
+ if (genDeclType == null && genDeclMethod == null)
+ {
+ // Probably from within an explicitly implemented interface member,
+ // where CSC uses parameter names instead of indices (why?), e.g.
+ // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
+ // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
+ buf.Append (type.Name);
+ }
+ if (buf.Length == l)
+ {
+ throw new Exception (string.Format (
+ "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
+ type.Name, genDeclType, genDeclMethod));
+ }
+ }
+ else
+ {
+ base.AppendTypeName (buf, type, context);
+ if (AddTypeCount)
+ {
+ int numArgs = type.GenericParameters.Count;
+ if (type.DeclaringType != null)
+ numArgs -= type.GenericParameters.Count;
+ if (numArgs > 0)
+ {
+ buf.Append ('`').Append (numArgs);
+ }
+ }
+ }
+ return buf;
+ }
+
+ protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
+ {
+ buf.Append (ArrayDelimeters[0]);
+ int rank = array.Rank;
+ if (rank > 1)
+ {
+ buf.Append ("0:");
+ for (int i = 1; i < rank; ++i)
+ {
+ buf.Append (",0:");
+ }
+ }
+ return buf.Append (ArrayDelimeters[1]);
+ }
+
+ protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
+ {
+ if (!AddTypeCount)
+ base.AppendGenericType (buf, type, context);
+ else
+ AppendType (buf, type, context);
+ return buf;
+ }
+
+ private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
+ {
+ List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
+ bool insertNested = false;
+ int prevParamCount = 0;
+ foreach (var decl in decls)
+ {
+ if (insertNested)
+ buf.Append (NestedTypeSeparator);
+ insertNested = true;
+ base.AppendTypeName (buf, decl, context);
+ int argCount = DocUtils.GetGenericArgumentCount (decl);
+ int numArgs = argCount - prevParamCount;
+ prevParamCount = argCount;
+ if (numArgs > 0)
+ buf.Append ('`').Append (numArgs);
+ }
+ return buf;
+ }
+
+ protected override string GetConstructorName (MethodReference constructor)
+ {
+ return GetMethodDefinitionName (constructor, "#ctor");
+ }
+
+ protected override string GetMethodName (MethodReference method)
+ {
+ string name = null;
+ MethodDefinition methodDef = method as MethodDefinition;
+ if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
+ name = method.Name;
+ else
+ {
+ TypeReference iface;
+ MethodReference ifaceMethod;
+ DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
+ AddTypeCount = false;
+ name = GetTypeName (iface) + "." + ifaceMethod.Name;
+ AddTypeCount = true;
+ }
+ return GetMethodDefinitionName (method, name);
+ }
+
+ private string GetMethodDefinitionName (MethodReference method, string name)
+ {
+ StringBuilder buf = new StringBuilder ();
+ buf.Append (GetTypeName (method.DeclaringType));
+ buf.Append ('.');
+ buf.Append (name.Replace (".", "#"));
+ if (method.IsGenericMethod ())
+ {
+ IList<GenericParameter> genArgs = method.GenericParameters;
+ if (genArgs.Count > 0)
+ buf.Append ("``").Append (genArgs.Count);
+ }
+ IList<ParameterDefinition> parameters = method.Parameters;
+ try
+ {
+ genDeclType = method.DeclaringType;
+ genDeclMethod = method;
+ AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
+ }
+ finally
+ {
+ genDeclType = null;
+ genDeclMethod = null;
+ }
+ return buf.ToString ();
+ }
+
+ private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
+ {
+ if (parameters.Count == 0)
+ return buf;
+
+ buf.Append ('(');
+
+ AppendParameter (buf, genArgs, parameters[0]);
+ for (int i = 1; i < parameters.Count; ++i)
+ {
+ buf.Append (',');
+ AppendParameter (buf, genArgs, parameters[i]);
+ }
+
+ return buf.Append (')');
+ }
+
+ private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
+ {
+ AddTypeCount = false;
+ buf.Append (GetTypeName (parameter.ParameterType));
+ AddTypeCount = true;
+ return buf;
+ }
+
+ protected override string GetPropertyName (PropertyReference property)
+ {
+ string name = null;
+
+ PropertyDefinition propertyDef = property as PropertyDefinition;
+ MethodDefinition method = null;
+ if (propertyDef != null)
+ method = propertyDef.GetMethod ?? propertyDef.SetMethod;
+ if (method != null && !DocUtils.IsExplicitlyImplemented (method))
+ name = property.Name;
+ else
+ {
+ TypeReference iface;
+ MethodReference ifaceMethod;
+ DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
+ AddTypeCount = false;
+ name = string.Join ("#", new string[]{
+ GetTypeName (iface).Replace (".", "#"),
+ DocUtils.GetMember (property.Name)
+ });
+ AddTypeCount = true;
+ }
+
+ StringBuilder buf = new StringBuilder ();
+ buf.Append (GetName (property.DeclaringType));
+ buf.Append ('.');
+ buf.Append (name);
+ IList<ParameterDefinition> parameters = property.Parameters;
+ if (parameters.Count > 0)
+ {
+ genDeclType = property.DeclaringType;
+ buf.Append ('(');
+ IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
+ AppendParameter (buf, genArgs, parameters[0]);
+ for (int i = 1; i < parameters.Count; ++i)
+ {
+ buf.Append (',');
+ AppendParameter (buf, genArgs, parameters[i]);
+ }
+ buf.Append (')');
+ genDeclType = null;
+ }
+ return buf.ToString ();
+ }
+
+ protected override string GetFieldName (FieldReference field)
+ {
+ return string.Format ("{0}.{1}",
+ GetName (field.DeclaringType), field.Name);
+ }
+
+ protected override string GetEventName (EventReference e)
+ {
+ return string.Format ("{0}.{1}",
+ GetName (e.DeclaringType), e.Name);
+ }
+
+ protected override string GetTypeDeclaration (TypeDefinition type)
+ {
+ string name = GetName (type);
+ if (type == null)
+ return null;
+ return "T:" + name;
+ }
+
+ protected override string GetConstructorDeclaration (MethodDefinition constructor)
+ {
+ string name = GetName (constructor);
+ if (name == null)
+ return null;
+ return "M:" + name;
+ }
+
+ protected override string GetMethodDeclaration (MethodDefinition method)
+ {
+ string name = GetName (method);
+ if (name == null)
+ return null;
+ if (method.Name == "op_Implicit" || method.Name == "op_Explicit")
+ {
+ genDeclType = method.DeclaringType;
+ genDeclMethod = method;
+ name += "~" + GetName (method.ReturnType);
+ genDeclType = null;
+ genDeclMethod = null;
+ }
+ return "M:" + name;
+ }
+
+ protected override string GetPropertyDeclaration (PropertyDefinition property)
+ {
+ string name = GetName (property);
+ if (name == null)
+ return null;
+ return "P:" + name;
+ }
+
+ protected override string GetFieldDeclaration (FieldDefinition field)
+ {
+ string name = GetName (field);
+ if (name == null)
+ return null;
+ return "F:" + name;
+ }
+
+ protected override string GetEventDeclaration (EventDefinition e)
+ {
+ string name = GetName (e);
+ if (name == null)
+ return null;
+ return "E:" + name;
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/Formatters/StandardFlagsEnumFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/StandardFlagsEnumFormatter.cs
new file mode 100644
index 00000000..8b5f65b5
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/Formatters/StandardFlagsEnumFormatter.cs
@@ -0,0 +1,34 @@
+using System.Linq;
+
+using Mono.Cecil;
+
+namespace Mono.Documentation.Updater
+{
+ /// <summary>Flags enum formatter that assumes powers of two values.</summary>
+ /// <remarks>As described here: https://msdn.microsoft.com/en-us/library/vstudio/ms229062(v=vs.100).aspx</remarks>
+ class StandardFlagsEnumFormatter : AttributeValueFormatter
+ {
+ public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
+ {
+ TypeReference valueType = type.Reference;
+ TypeDefinition valueDef = type.Definition;
+ if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute"))
+ {
+
+ string typename = MDocUpdater.GetDocTypeFullName (valueType);
+ var values = MDocUpdater.GetEnumerationValues (valueDef);
+ long c = MDocUpdater.ToInt64 (v);
+ returnvalue = string.Join (" | ",
+ (from i in values.Keys
+ where (c & i) == i && i != 0
+ select typename + "." + values[i])
+ .DefaultIfEmpty (c.ToString ()).ToArray ());
+
+ return true;
+ }
+
+ returnvalue = null;
+ return false;
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Frameworks/AssemblySet.cs b/mdoc/Mono.Documentation/Updater/Frameworks/AssemblySet.cs
index 4d62d3a1..524e39a7 100644
--- a/mdoc/Mono.Documentation/Frameworks/AssemblySet.cs
+++ b/mdoc/Mono.Documentation/Updater/Frameworks/AssemblySet.cs
@@ -5,7 +5,7 @@ using System.IO;
using System.Linq;
using Mono.Cecil;
-namespace Mono.Documentation
+namespace Mono.Documentation.Updater.Frameworks
{
/// <summary>
/// Represents a set of assemblies that we want to document
diff --git a/mdoc/Mono.Documentation/Frameworks/FrameworkEntry.cs b/mdoc/Mono.Documentation/Updater/Frameworks/FrameworkEntry.cs
index c6710f09..c6684d51 100644
--- a/mdoc/Mono.Documentation/Frameworks/FrameworkEntry.cs
+++ b/mdoc/Mono.Documentation/Updater/Frameworks/FrameworkEntry.cs
@@ -4,7 +4,7 @@ using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Rocks;
-namespace Mono.Documentation
+namespace Mono.Documentation.Updater.Frameworks
{
class FrameworkEntry
{
diff --git a/mdoc/Mono.Documentation/Frameworks/FrameworkIndex.cs b/mdoc/Mono.Documentation/Updater/Frameworks/FrameworkIndex.cs
index d8bbf127..e392f585 100644
--- a/mdoc/Mono.Documentation/Frameworks/FrameworkIndex.cs
+++ b/mdoc/Mono.Documentation/Updater/Frameworks/FrameworkIndex.cs
@@ -7,7 +7,7 @@ using System.Xml.Linq;
using Mono.Cecil;
-namespace Mono.Documentation
+namespace Mono.Documentation.Updater.Frameworks
{
class FrameworkIndex
diff --git a/mdoc/Mono.Documentation/Frameworks/FrameworkTypeEntry.cs b/mdoc/Mono.Documentation/Updater/Frameworks/FrameworkTypeEntry.cs
index 3108f11c..67bcee86 100644
--- a/mdoc/Mono.Documentation/Frameworks/FrameworkTypeEntry.cs
+++ b/mdoc/Mono.Documentation/Updater/Frameworks/FrameworkTypeEntry.cs
@@ -3,7 +3,7 @@ using System.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Rocks;
-namespace Mono.Documentation
+namespace Mono.Documentation.Updater.Frameworks
{
class FrameworkTypeEntry : IComparable<FrameworkTypeEntry>
{
diff --git a/mdoc/Mono.Documentation/Frameworks/UwpResolver.cs b/mdoc/Mono.Documentation/Updater/Frameworks/UwpResolver.cs
index 40f490ec..4dbbec82 100644
--- a/mdoc/Mono.Documentation/Frameworks/UwpResolver.cs
+++ b/mdoc/Mono.Documentation/Updater/Frameworks/UwpResolver.cs
@@ -4,7 +4,7 @@ using System.IO;
using Microsoft.Win32;
using Mono.Cecil;
-namespace Mono.Documentation.Frameworks
+namespace Mono.Documentation.Updater.Frameworks
{
/// <summary>Mono.Cecil resolver for the windows universal platform</summary>
class UwpResolver : DefaultAssemblyResolver
diff --git a/mdoc/Mono.Documentation/Updater/MemberFormatterState.cs b/mdoc/Mono.Documentation/Updater/MemberFormatterState.cs
new file mode 100644
index 00000000..373c1c94
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/MemberFormatterState.cs
@@ -0,0 +1,8 @@
+namespace Mono.Documentation.Updater
+{
+ public enum MemberFormatterState
+ {
+ None,
+ WithinGenericTypeParameters,
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/MsxdocDocumentationImporter.cs b/mdoc/Mono.Documentation/Updater/MsxdocDocumentationImporter.cs
new file mode 100644
index 00000000..37dadfa2
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/MsxdocDocumentationImporter.cs
@@ -0,0 +1,135 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Xml;
+
+using Mono.Cecil;
+
+namespace Mono.Documentation.Updater
+{
+ class MsxdocDocumentationImporter : DocumentationImporter
+ {
+
+ XmlDocument slashdocs;
+
+ public MsxdocDocumentationImporter (string file)
+ {
+ try
+ {
+ char oppositeSlash = Path.DirectorySeparatorChar == '/' ? '\\' : '/';
+ if (file.Contains (oppositeSlash))
+ file = file.Replace (oppositeSlash, Path.DirectorySeparatorChar);
+
+ var xml = File.ReadAllText (file);
+
+ // Ensure Unix line endings
+ xml = xml.Replace ("\r", "");
+
+ slashdocs = new XmlDocument ();
+
+ slashdocs.LoadXml (xml);
+ }
+ catch (IOException ex)
+ {
+ Console.WriteLine ($"Importer Error: {ex.Message}");
+ }
+ }
+
+ public override void ImportDocumentation (DocsNodeInfo info)
+ {
+ XmlNode elem = GetDocs (info.Member ?? info.Type);
+
+ if (elem == null)
+ return;
+
+ XmlElement e = info.Node;
+
+ if (elem.SelectSingleNode ("summary") != null)
+ MDocUpdater.ClearElement (e, "summary");
+ if (elem.SelectSingleNode ("remarks") != null)
+ MDocUpdater.ClearElement (e, "remarks");
+ if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null)
+ {
+ MDocUpdater.ClearElement (e, "value");
+ MDocUpdater.ClearElement (e, "returns");
+ }
+
+ foreach (XmlNode child in elem.ChildNodes)
+ {
+ switch (child.Name)
+ {
+ case "param":
+ case "typeparam":
+ {
+ XmlAttribute name = child.Attributes["name"];
+ if (name == null)
+ break;
+ XmlElement p2 = (XmlElement)e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
+ if (p2 != null)
+ p2.InnerXml = child.InnerXml;
+ break;
+ }
+ // Occasionally XML documentation will use <returns/> on
+ // properties, so let's try to normalize things.
+ case "value":
+ case "returns":
+ {
+ XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
+ v.InnerXml = child.InnerXml;
+ e.AppendChild (v);
+ break;
+ }
+ case "altmember":
+ case "exception":
+ case "permission":
+ {
+ XmlAttribute cref = child.Attributes["cref"] ?? child.Attributes["name"];
+ if (cref == null)
+ break;
+ XmlElement a = (XmlElement)e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
+ if (a == null)
+ {
+ a = e.OwnerDocument.CreateElement (child.Name);
+ a.SetAttribute ("cref", cref.Value);
+ e.AppendChild (a);
+ }
+ a.InnerXml = child.InnerXml;
+ break;
+ }
+ case "seealso":
+ {
+ XmlAttribute cref = child.Attributes["cref"];
+ if (cref == null)
+ break;
+ XmlElement a = (XmlElement)e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
+ if (a == null)
+ {
+ a = e.OwnerDocument.CreateElement ("altmember");
+ a.SetAttribute ("cref", cref.Value);
+ e.AppendChild (a);
+ }
+ break;
+ }
+ default:
+ {
+ bool add = true;
+ if (child.NodeType == XmlNodeType.Element &&
+ e.SelectNodes (child.Name).Cast<XmlElement> ().Any (n => n.OuterXml == child.OuterXml))
+ add = false;
+ if (add)
+ MDocUpdater.CopyNode (child, e);
+ break;
+ }
+ }
+ }
+ }
+
+ private XmlNode GetDocs (MemberReference member)
+ {
+ string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
+ if (slashdocsig != null && slashdocs != null)
+ return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
+ return null;
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/ResolvedTypeInfo.cs b/mdoc/Mono.Documentation/Updater/ResolvedTypeInfo.cs
new file mode 100644
index 00000000..0db9b6da
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/ResolvedTypeInfo.cs
@@ -0,0 +1,29 @@
+
+using Mono.Cecil;
+
+namespace Mono.Documentation.Updater
+{
+ class ResolvedTypeInfo
+ {
+ TypeDefinition typeDef;
+
+ public ResolvedTypeInfo (TypeReference value)
+ {
+ Reference = value;
+ }
+
+ public TypeReference Reference { get; private set; }
+
+ public TypeDefinition Definition
+ {
+ get
+ {
+ if (typeDef == null)
+ {
+ typeDef = Reference.Resolve ();
+ }
+ return typeDef;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Util/ApiStyle.cs b/mdoc/Mono.Documentation/Util/ApiStyle.cs
new file mode 100644
index 00000000..3a721bf2
--- /dev/null
+++ b/mdoc/Mono.Documentation/Util/ApiStyle.cs
@@ -0,0 +1,8 @@
+namespace Mono.Documentation.Util
+{
+ enum ApiStyle
+ {
+ Classic,
+ Unified
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Util/CecilExtensions.cs b/mdoc/Mono.Documentation/Util/CecilExtensions.cs
new file mode 100644
index 00000000..acccd0f7
--- /dev/null
+++ b/mdoc/Mono.Documentation/Util/CecilExtensions.cs
@@ -0,0 +1,120 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using Mono.Cecil;
+
+namespace Mono.Documentation.Util
+{
+ static class CecilExtensions
+ {
+ public static string GetDeclaringType (this CustomAttribute attribute)
+ {
+ var type = attribute.Constructor.DeclaringType;
+ var typeName = type.FullName;
+
+ string translatedType = NativeTypeManager.GetTranslatedName (type);
+ return translatedType;
+ }
+
+ public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
+ {
+ foreach (var c in type.Methods.Where (m => m.IsConstructor))
+ yield return (MemberReference)c;
+ foreach (var e in type.Events)
+ yield return (MemberReference)e;
+ foreach (var f in type.Fields)
+ yield return (MemberReference)f;
+ foreach (var m in type.Methods.Where (m => !m.IsConstructor))
+ yield return (MemberReference)m;
+ foreach (var t in type.NestedTypes)
+ yield return (MemberReference)t;
+ foreach (var p in type.Properties)
+ yield return (MemberReference)p;
+ }
+
+ public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
+ {
+ return GetMembers (type).Where (m => m.Name == member);
+ }
+
+ public static MemberReference GetMember (this TypeDefinition type, string member)
+ {
+ return GetMembers (type, member).EnsureZeroOrOne ();
+ }
+
+ static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
+ {
+ if (source.Count () > 1)
+ throw new InvalidOperationException ("too many matches");
+ return source.FirstOrDefault ();
+ }
+
+ public static MethodDefinition GetMethod (this TypeDefinition type, string method)
+ {
+ return type.Methods
+ .Where (m => m.Name == method)
+ .EnsureZeroOrOne ();
+ }
+
+ public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
+ {
+ TypeDefinition def = type as TypeDefinition;
+ if (def == null)
+ return new MemberReference[0];
+ CustomAttribute defMemberAttr = def.CustomAttributes
+ .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
+ if (defMemberAttr == null)
+ return new MemberReference[0];
+ string name = (string)defMemberAttr.ConstructorArguments[0].Value;
+ return def.Properties
+ .Where (p => p.Name == name)
+ .Select (p => (MemberReference)p);
+ }
+
+ public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
+ {
+ return assembly.Modules.SelectMany (md => md.GetAllTypes ());
+ }
+
+ public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
+ {
+ return GetTypes (assembly)
+ .Where (td => td.FullName == type)
+ .EnsureZeroOrOne ();
+ }
+
+ public static bool IsGenericType (this TypeReference type)
+ {
+ return type.GenericParameters.Count > 0;
+ }
+
+ public static bool IsGenericMethod (this MethodReference method)
+ {
+ return method.GenericParameters.Count > 0;
+ }
+
+ public static TypeReference GetUnderlyingType (this TypeDefinition type)
+ {
+ if (!type.IsEnum)
+ return type;
+ return type.Fields.First (f => f.Name == "value__").FieldType;
+ }
+
+ public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
+ {
+ return self.Types.SelectMany (t => t.GetAllTypes ());
+ }
+
+ static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
+ {
+ yield return self;
+
+ if (!self.HasNestedTypes)
+ yield break;
+
+ foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
+ yield return type;
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Util/NativeTypeManager.cs b/mdoc/Mono.Documentation/Util/NativeTypeManager.cs
new file mode 100644
index 00000000..6c3e1118
--- /dev/null
+++ b/mdoc/Mono.Documentation/Util/NativeTypeManager.cs
@@ -0,0 +1,125 @@
+using System.Collections.Generic;
+
+using Mono.Cecil;
+
+namespace Mono.Documentation.Util
+{
+ static class NativeTypeManager
+ {
+
+ static Dictionary<string, string> toNativeType = new Dictionary<string, string> (){
+
+ {"int", "nint"},
+ {"Int32", "nint"},
+ {"System.Int32", "System.nint"},
+ {"uint", "nuint"},
+ {"UInt32", "nuint"},
+ {"System.UInt32", "System.nuint"},
+ {"float", "nfloat"},
+ {"Single", "nfloat"},
+ {"System.Single", "System.nfloat"},
+ {"SizeF", "CoreGraphics.CGSize"},
+ {"System.Drawing.SizeF", "CoreGraphics.CGSize"},
+ {"PointF", "CoreGraphics.CGPoint"},
+ {"System.Drawing.PointF", "CoreGraphics.CGPoint"},
+ {"RectangleF", "CoreGraphics.CGRect" },
+ {"System.Drawing.RectangleF", "CoreGraphics.CGRect"}
+ };
+
+ static Dictionary<string, string> fromNativeType = new Dictionary<string, string> (){
+
+ {"nint", "int"},
+ {"System.nint", "System.Int32"},
+ {"nuint", "uint"},
+ {"System.nuint", "System.UInt32"},
+ {"nfloat", "float"},
+ {"System.nfloat", "System.Single"},
+ {"CoreGraphics.CGSize", "System.Drawing.SizeF"},
+ {"CoreGraphics.CGPoint", "System.Drawing.PointF"},
+ {"CoreGraphics.CGRect", "System.Drawing.RectangleF"},
+ {"MonoTouch.CoreGraphics.CGSize", "System.Drawing.SizeF"},
+ {"MonoTouch.CoreGraphics.CGPoint", "System.Drawing.PointF"},
+ {"MonoTouch.CoreGraphics.CGRect", "System.Drawing.RectangleF"}
+ };
+
+ public static string ConvertToNativeType (string typename)
+ {
+ string nvalue;
+
+ bool isOut = false;
+ bool isArray = false;
+ string valueToCompare = StripToComparableType (typename, ref isOut, ref isArray);
+
+ if (toNativeType.TryGetValue (valueToCompare, out nvalue))
+ {
+
+ if (isArray)
+ {
+ nvalue += "[]";
+ }
+ if (isOut)
+ {
+ nvalue += "&";
+ }
+ return nvalue;
+ }
+ return typename;
+ }
+ public static string ConvertFromNativeType (string typename)
+ {
+ string nvalue;
+
+ bool isOut = false;
+ bool isArray = false;
+ string valueToCompare = StripToComparableType (typename, ref isOut, ref isArray);
+
+ if (fromNativeType.TryGetValue (valueToCompare, out nvalue))
+ {
+ if (isArray)
+ {
+ nvalue += "[]";
+ }
+ if (isOut)
+ {
+ nvalue += "&";
+ }
+ return nvalue;
+ }
+ // it wasn't one of the native types ... just return it
+ return typename;
+ }
+
+ static string StripToComparableType (string typename, ref bool isOut, ref bool isArray)
+ {
+ string valueToCompare = typename;
+ if (typename.EndsWith ("[]"))
+ {
+ valueToCompare = typename.Substring (0, typename.Length - 2);
+ isArray = true;
+ }
+ if (typename.EndsWith ("&"))
+ {
+ valueToCompare = typename.Substring (0, typename.Length - 1);
+ isOut = true;
+ }
+ if (typename.Contains ("<"))
+ {
+ // TODO: Need to recursively process generic parameters
+ }
+ return valueToCompare;
+ }
+
+ public static string GetTranslatedName (TypeReference t)
+ {
+ string typename = t.FullName;
+
+ bool isInAssembly = MDocUpdater.IsInAssemblies (t.Module.Name);
+ if (isInAssembly && !typename.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (t))
+ {
+ string nameWithDropped = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typename);
+ return nameWithDropped;
+ }
+ return typename;
+ }
+ }
+} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/assembler.cs b/mdoc/Mono.Documentation/assembler.cs
index c8c89158..97ebe5b1 100644
--- a/mdoc/Mono.Documentation/assembler.cs
+++ b/mdoc/Mono.Documentation/assembler.cs
@@ -17,6 +17,7 @@ using System.IO;
using System.Xml.Linq;
using System.Xml.XPath;
using Monodoc.Ecma;
+using Mono.Documentation.Util;
namespace Mono.Documentation {
diff --git a/mdoc/Mono.Documentation/exceptions.cs b/mdoc/Mono.Documentation/exceptions.cs
index 23b6762a..47738499 100644
--- a/mdoc/Mono.Documentation/exceptions.cs
+++ b/mdoc/Mono.Documentation/exceptions.cs
@@ -32,7 +32,8 @@ using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
-
+using Mono.Documentation.Updater;
+
namespace Mono.Documentation {
[Flags]
diff --git a/mdoc/Mono.Documentation/monodocer.cs b/mdoc/Mono.Documentation/monodocer.cs
deleted file mode 100644
index b7c8520f..00000000
--- a/mdoc/Mono.Documentation/monodocer.cs
+++ /dev/null
@@ -1,6451 +0,0 @@
-// Updater program for syncing Mono's ECMA-style documentation files
-// with an assembly.
-// By Joshua Tauberer <tauberer@for.net>
-
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Diagnostics;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Xml;
-using System.Xml.Linq;
-using System.Xml.XPath;
-
-using Mono.Cecil;
-using Mono.Cecil.Rocks;
-using Mono.Options;
-
-using MyXmlNodeList = System.Collections.Generic.List<System.Xml.XmlNode>;
-using StringList = System.Collections.Generic.List<string>;
-using StringToStringMap = System.Collections.Generic.Dictionary<string, string>;
-using StringToXmlNodeMap = System.Collections.Generic.Dictionary<string, System.Xml.XmlNode>;
-
-namespace Mono.Documentation {
- static class NativeTypeManager {
-
- static Dictionary<string, string> toNativeType = new Dictionary<string,string>(){
-
- {"int", "nint"},
- {"Int32", "nint"},
- {"System.Int32", "System.nint"},
- {"uint", "nuint"},
- {"UInt32", "nuint"},
- {"System.UInt32", "System.nuint"},
- {"float", "nfloat"},
- {"Single", "nfloat"},
- {"System.Single", "System.nfloat"},
- {"SizeF", "CoreGraphics.CGSize"},
- {"System.Drawing.SizeF", "CoreGraphics.CGSize"},
- {"PointF", "CoreGraphics.CGPoint"},
- {"System.Drawing.PointF", "CoreGraphics.CGPoint"},
- {"RectangleF", "CoreGraphics.CGRect" },
- {"System.Drawing.RectangleF", "CoreGraphics.CGRect"}
- };
-
- static Dictionary<string, string> fromNativeType = new Dictionary<string,string>(){
-
- {"nint", "int"},
- {"System.nint", "System.Int32"},
- {"nuint", "uint"},
- {"System.nuint", "System.UInt32"},
- {"nfloat", "float"},
- {"System.nfloat", "System.Single"},
- {"CoreGraphics.CGSize", "System.Drawing.SizeF"},
- {"CoreGraphics.CGPoint", "System.Drawing.PointF"},
- {"CoreGraphics.CGRect", "System.Drawing.RectangleF"},
- {"MonoTouch.CoreGraphics.CGSize", "System.Drawing.SizeF"},
- {"MonoTouch.CoreGraphics.CGPoint", "System.Drawing.PointF"},
- {"MonoTouch.CoreGraphics.CGRect", "System.Drawing.RectangleF"}
- };
-
- public static string ConvertToNativeType(string typename) {
- string nvalue;
-
- bool isOut=false;
- bool isArray=false;
- string valueToCompare = StripToComparableType (typename, ref isOut, ref isArray);
-
- if (toNativeType.TryGetValue (valueToCompare, out nvalue)) {
-
- if (isArray) {
- nvalue += "[]";
- }
- if (isOut) {
- nvalue += "&";
- }
- return nvalue;
- }
- return typename;
- }
- public static string ConvertFromNativeType(string typename) {
- string nvalue;
-
- bool isOut=false;
- bool isArray=false;
- string valueToCompare = StripToComparableType (typename, ref isOut, ref isArray);
-
- if (fromNativeType.TryGetValue (valueToCompare, out nvalue)) {
- if (isArray) {
- nvalue += "[]";
- }
- if (isOut) {
- nvalue += "&";
- }
- return nvalue;
- }
- // it wasn't one of the native types ... just return it
- return typename;
- }
-
- static string StripToComparableType (string typename, ref bool isOut, ref bool isArray)
- {
- string valueToCompare = typename;
- if (typename.EndsWith ("[]")) {
- valueToCompare = typename.Substring (0, typename.Length - 2);
- isArray = true;
- }
- if (typename.EndsWith ("&")) {
- valueToCompare = typename.Substring (0, typename.Length - 1);
- isOut = true;
- }
- if (typename.Contains ("<")) {
- // TODO: Need to recursively process generic parameters
- }
- return valueToCompare;
- }
-
- public static string GetTranslatedName(TypeReference t) {
- string typename = t.FullName;
-
- bool isInAssembly = MDocUpdater.IsInAssemblies (t.Module.Name);
- if (isInAssembly && !typename.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (t)) {
- string nameWithDropped = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typename);
- return nameWithDropped;
- }
- return typename;
- }
- }
-class MDocUpdater : MDocCommand
-{
- string srcPath;
- List<AssemblySet> assemblies = new List<AssemblySet>();
- StringList globalSearchPaths = new StringList ();
-
- string apistyle = string.Empty;
- bool isClassicRun;
-
- bool delete;
- bool show_exceptions;
- bool no_assembly_versions, ignore_missing_types;
- ExceptionLocations? exceptions;
-
- internal int additions = 0, deletions = 0;
-
- List<DocumentationImporter> importers = new List<DocumentationImporter> ();
-
- DocumentationEnumerator docEnum;
-
- string since;
-
- static readonly MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
- static readonly MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
-
- static MemberFormatter[] typeFormatters = new MemberFormatter[]{
- new CSharpMemberFormatter (),
- new ILMemberFormatter (),
- };
-
- static MemberFormatter[] memberFormatters = new MemberFormatter[]{
- new CSharpFullMemberFormatter (),
- new ILFullMemberFormatter (),
- };
-
- internal static readonly MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
-
- MyXmlNodeList extensionMethods = new MyXmlNodeList ();
-
- public static string droppedNamespace = string.Empty;
-
- public static bool HasDroppedNamespace(TypeDefinition forType)
- {
- return HasDroppedNamespace(forType.Module);
- }
-
- public static bool HasDroppedNamespace(MemberReference forMember)
- {
- return HasDroppedNamespace(forMember.Module);
- }
-
- public static bool HasDroppedNamespace(AssemblyDefinition forAssembly)
- {
- return HasDroppedNamespace(forAssembly.MainModule);
- }
-
- public static bool HasDroppedNamespace(ModuleDefinition forModule)
- {
- return !string.IsNullOrWhiteSpace (droppedNamespace) && droppedAssemblies.Any(da => da == forModule.Name);
- }
-
- public static bool HasDroppedAnyNamespace ()
- {
- return !string.IsNullOrWhiteSpace (droppedNamespace);
- }
-
- /// <summary>Logic flag to signify that we should list assemblies at the method level, since there are multiple
- /// assemblies for a given type/method.</summary>
- public bool IsMultiAssembly {
- get {
- return apistyle == "classic" || apistyle == "unified" || !string.IsNullOrWhiteSpace(FrameworksPath);
- }
- }
-
- /// <summary>Path which contains multiple folders with assemblies. Each folder contained will represent one framework.</summary>
- string FrameworksPath = string.Empty;
- FrameworkIndex frameworks;
- FrameworkIndex frameworksCache;
-
- static List<string> droppedAssemblies = new List<string>();
-
- public string PreserveTag { get; set; }
- public bool DisableSearchDirectoryRecurse = false;
- public static MDocUpdater Instance { get; private set; }
- public static bool SwitchingToMagicTypes { get; private set; }
-
- public override void Run (IEnumerable<string> args)
- {
- Instance = this;
- show_exceptions = DebugOutput;
- var types = new List<string> ();
- var p = new OptionSet () {
- { "delete",
- "Delete removed members from the XML files.",
- v => delete = v != null },
- { "exceptions:",
- "Document potential exceptions that members can generate. {SOURCES} " +
- "is a comma-separated list of:\n" +
- " asm Method calls in same assembly\n" +
- " depasm Method calls in dependent assemblies\n" +
- " all Record all possible exceptions\n" +
- " added Modifier; only create <exception/>s\n" +
- " for NEW types/members\n" +
- "If nothing is specified, then only exceptions from the member will " +
- "be listed.",
- v => exceptions = ParseExceptionLocations (v) },
- { "f=",
- "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
- v => {
- switch (v) {
- case "ignore-missing-types":
- ignore_missing_types = true;
- break;
- case "no-assembly-versions":
- no_assembly_versions = true;
- break;
- default:
- throw new Exception ("Unsupported flag `" + v + "'.");
- }
- } },
- { "fignore-missing-types",
- "Do not report an error if a --type=TYPE type\nwas not found.",
- v => ignore_missing_types = v != null },
- { "fno-assembly-versions",
- "Do not generate //AssemblyVersion elements.",
- v => no_assembly_versions = v != null },
- { "i|import=",
- "Import documentation from {FILE}.",
- v => AddImporter (v) },
- { "L|lib=",
- "Check for assembly references in {DIRECTORY}.",
- v => globalSearchPaths.Add (v) },
- { "library=",
- "Ignored for compatibility with update-ecma-xml.",
- v => {} },
- { "o|out=",
- "Root {DIRECTORY} to generate/update documentation.",
- v => srcPath = v },
- { "r=",
- "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
- "(Equivalent to '-L `dirname ASSEMBLY`'.)",
- v => globalSearchPaths.Add (Path.GetDirectoryName (v)) },
- { "since=",
- "Manually specify the assembly {VERSION} that new members were added in.",
- v => since = v },
- { "type=",
- "Only update documentation for {TYPE}.",
- v => types.Add (v) },
- { "dropns=",
- "When processing assembly {ASSEMBLY}, strip off leading namespace {PREFIX}:\n" +
- " e.g. --dropns ASSEMBLY=PREFIX",
- v => {
- var parts = v.Split ('=');
- if (parts.Length != 2) { Console.Error.WriteLine ("Invalid dropns input"); return; }
- var assembly = Path.GetFileName (parts [0].Trim ());
- var prefix = parts [1].Trim();
- droppedAssemblies.Add (assembly);
- droppedNamespace = prefix;
- } },
- { "ntypes",
- "If the new assembly is switching to 'magic types', then this switch should be defined.",
- v => SwitchingToMagicTypes = true },
- { "preserve",
- "Do not delete members that don't exist in the assembly, but rather mark them as preserved.",
- v => PreserveTag = "true" },
- { "api-style=",
- "Denotes the apistyle. Currently, only `classic` and `unified` are supported. `classic` set of assemblies should be run first, immediately followed by 'unified' assemblies with the `dropns` parameter.",
- v => apistyle = v.ToLowerInvariant () },
- { "fx|frameworks=",
- "Configuration XML file, that points to directories which contain libraries that span multiple frameworks.",
- v => FrameworksPath = v },
- { "use-docid",
- "Add 'DocId' to the list of type and member signatures",
- v =>
- {
- typeFormatters = typeFormatters.Union (new MemberFormatter[] { new DocIdFormatter () }).ToArray ();
- memberFormatters = memberFormatters.Union (new MemberFormatter[] { new DocIdFormatter () }).ToArray ();
- } },
- { "disable-searchdir-recurse",
- "Default behavior for adding search directories ('-L') is to recurse them and search in all subdirectories. This disables that",
- v => DisableSearchDirectoryRecurse = true },
- };
- var assemblyPaths = Parse (p, args, "update",
- "[OPTIONS]+ ASSEMBLIES",
- "Create or update documentation from ASSEMBLIES.");
-
- if (!string.IsNullOrWhiteSpace (FrameworksPath)) {
- var configPath = FrameworksPath;
- var frameworksDir = FrameworksPath;
- if (!configPath.EndsWith ("frameworks.xml", StringComparison.InvariantCultureIgnoreCase))
- configPath = Path.Combine (configPath, "frameworks.xml");
- else
- frameworksDir = Path.GetDirectoryName (configPath);
-
- var fxconfig = XDocument.Load (configPath);
- var fxd = fxconfig.Root
- .Elements ("Framework")
- .Select (f => new {
- Name = f.Attribute ("Name").Value,
- Path = Path.Combine(frameworksDir,f.Attribute("Source").Value),
- SearchPaths = f.Elements("assemblySearchPath")
- .Select(a => Path.Combine(frameworksDir, a.Value))
- .ToArray(),
- Imports = f.Elements("import")
- .Select(a => Path.Combine(frameworksDir, a.Value))
- .ToArray()
- })
- .Where (f => Directory.Exists (f.Path));
-
- Func<string, string, IEnumerable<string>> getFiles = (string path, string filters) => {
- return filters
- .Split ('|')
- .SelectMany (v => Directory.GetFiles (path, v));
- };
-
- var sets = fxd.Select (d => new AssemblySet (
- d.Name,
- getFiles (d.Path, "*.dll|*.exe|*.winmd"),
- this.globalSearchPaths.Union (d.SearchPaths),
- d.Imports
- ));
- this.assemblies.AddRange (sets);
- assemblyPaths.AddRange (sets.SelectMany (s => s.AssemblyPaths));
-
- // Create a cache of all frameworks, so we can look up
- // members that may exist only other frameworks before deleting them
- Console.Write ("Creating frameworks cache: ");
- FrameworkIndex cacheIndex = new FrameworkIndex (FrameworksPath);
- string[] prefixesToAvoid = { "get_", "set_", "add_", "remove_", "raise_" };
- foreach (var assemblySet in this.assemblies) {
- using (assemblySet) {
- Console.Write (".");
- foreach (var assembly in assemblySet.Assemblies) {
- var a = cacheIndex.StartProcessingAssembly(assembly, assemblySet.Importers);
- foreach (var type in assembly.GetTypes ()) {
- var t = a.ProcessType (type);
- foreach (var member in type.GetMembers ().Where (m => !prefixesToAvoid.Any (pre => m.Name.StartsWith (pre, StringComparison.Ordinal))))
- t.ProcessMember (member);
- }
- }
- }
- }
- Console.WriteLine($"{Environment.NewLine}done caching.");
- this.frameworksCache = cacheIndex;
- }
- else {
- this.assemblies.Add (new AssemblySet ("Default", assemblyPaths, this.globalSearchPaths, null));
- }
-
- if (assemblyPaths == null)
- return;
- if (assemblyPaths.Count == 0)
- Error ("No assemblies specified.");
-
- if (!DisableSearchDirectoryRecurse) {
- // unless it's been explicitly disabled, let's
- // add all of the subdirectories to the resolver
- // search paths.
- foreach (var assemblySet in this.assemblies)
- assemblySet.RecurseSearchDirectories ();
- }
-
- // validation for the api-style parameter
- if (apistyle == "classic")
- isClassicRun = true;
- else if (apistyle == "unified") {
- if (!droppedAssemblies.Any ())
- Error ("api-style 'unified' must also supply the 'dropns' parameter with at least one assembly and dropped namespace.");
- } else if (!string.IsNullOrWhiteSpace (apistyle))
- Error ("api-style '{0}' is not currently supported", apistyle);
-
- // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
-
- if (srcPath == null)
- throw new InvalidOperationException("The --out option is required.");
-
- docEnum = docEnum ?? new DocumentationEnumerator ();
-
- // PERFORM THE UPDATES
- frameworks = new FrameworkIndex (FrameworksPath);
-
- if (types.Count > 0) {
- types.Sort ();
- DoUpdateTypes (srcPath, types, srcPath);
- }
- else
- DoUpdateAssemblies (srcPath, srcPath);
-
- if (!string.IsNullOrWhiteSpace (FrameworksPath))
- frameworks.WriteToDisk (srcPath);
-
- Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
- }
-
- public static bool IsInAssemblies(string name) {
- return Instance?.assemblies != null ? Instance.assemblies.Any(a => a.Contains(name)) : true;
- }
-
- void AddImporter (string path)
- {
- var importer = GetImporter (path, supportsEcmaDoc:true);
- if (importer != null)
- importers.Add (importer);
- }
-
- internal DocumentationImporter GetImporter (string path, bool supportsEcmaDoc)
- {
- try {
- XmlReader r = new XmlTextReader (path);
- if (r.Read ()) {
- while (r.NodeType != XmlNodeType.Element) {
- if (!r.Read ())
- Error ("Unable to read XML file: {0}.", path);
- }
- if (r.LocalName == "doc") {
- return new MsxdocDocumentationImporter (path);
- }
- else if (r.LocalName == "Libraries") {
- if (!supportsEcmaDoc)
- throw new NotSupportedException ($"Ecma documentation not supported in this mode: {path}");
-
- var ecmadocs = new XmlTextReader (path);
- docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
- return new EcmaDocumentationImporter (ecmadocs);
- }
- else
- Error ("Unsupported XML format within {0}.", path);
- }
- r.Close ();
- } catch (Exception e) {
- Environment.ExitCode = 1;
- Error ("Could not load XML file: {0}.", e.Message);
- }
- return null;
- }
-
- static ExceptionLocations ParseExceptionLocations (string s)
- {
- ExceptionLocations loc = ExceptionLocations.Member;
- if (s == null)
- return loc;
- foreach (var type in s.Split (',')) {
- switch (type) {
- case "added": loc |= ExceptionLocations.AddedMembers; break;
- case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
- case "asm": loc |= ExceptionLocations.Assembly; break;
- case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
- default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
- }
- }
- return loc;
- }
-
- internal void Warning (string format, params object[] args)
- {
- Message (TraceLevel.Warning, "mdoc: " + format, args);
- }
-
- internal AssemblyDefinition LoadAssembly (string name, IAssemblyResolver assemblyResolver)
- {
- AssemblyDefinition assembly = null;
- try {
- assembly = AssemblyDefinition.ReadAssembly (name, new ReaderParameters { AssemblyResolver = assemblyResolver });
- }
- catch (Exception ex) {
- Warning ($"Unable to load assembly '{name}': {ex.Message}");
- }
-
- return assembly;
- }
-
- private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
- OrderTypeAttributes (element);
- XmlTextWriter writer = new XmlTextWriter(output);
- writer.Formatting = Formatting.Indented;
- writer.Indentation = 2;
- writer.IndentChar = ' ';
- element.WriteTo(writer);
- output.WriteLine();
- }
-
- private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
- {
- Action<string> creator = file => {
- using (var writer = OpenWrite (file, mode))
- action (writer);
- };
-
- MdocFile.UpdateFile (filename, creator);
- }
-
- private static void OrderTypeAttributes (XmlElement e)
- {
- foreach (XmlElement type in e.SelectNodes ("//Type")) {
- OrderTypeAttributes (type.Attributes);
- }
- }
-
- static readonly string[] TypeAttributeOrder = {
- "Name", "FullName", "FullNameSP", "Maintainer"
- };
-
- private static void OrderTypeAttributes (XmlAttributeCollection c)
- {
- XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
- for (int i = 0; i < c.Count; ++i) {
- XmlAttribute a = c [i];
- for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
- if (a.Name == TypeAttributeOrder [j]) {
- attrs [j] = a;
- break;
- }
- }
- }
- for (int i = attrs.Length-1; i >= 0; --i) {
- XmlAttribute n = attrs [i];
- if (n == null)
- continue;
- XmlAttribute r = null;
- for (int j = i+1; j < attrs.Length; ++j) {
- if (attrs [j] != null) {
- r = attrs [j];
- break;
- }
- }
- if (r == null)
- continue;
- if (c [n.Name] != null) {
- c.RemoveNamedItem (n.Name);
- c.InsertBefore (n, r);
- }
- }
- }
-
- private XmlDocument CreateIndexStub()
- {
- XmlDocument index = new XmlDocument();
-
- XmlElement index_root = index.CreateElement("Overview");
- index.AppendChild(index_root);
-
- if (assemblies.Count == 0)
- throw new Exception ("No assembly");
-
- XmlElement index_assemblies = index.CreateElement("Assemblies");
- index_root.AppendChild(index_assemblies);
-
- XmlElement index_remarks = index.CreateElement("Remarks");
- index_remarks.InnerText = "To be added.";
- index_root.AppendChild(index_remarks);
-
- XmlElement index_copyright = index.CreateElement("Copyright");
- index_copyright.InnerText = "To be added.";
- index_root.AppendChild(index_copyright);
-
- XmlElement index_types = index.CreateElement("Types");
- index_root.AppendChild(index_types);
-
- return index;
- }
-
- private static void WriteNamespaceStub(string ns, string outdir) {
- XmlDocument index = new XmlDocument();
-
- XmlElement index_root = index.CreateElement("Namespace");
- index.AppendChild(index_root);
-
- index_root.SetAttribute("Name", ns);
-
- XmlElement index_docs = index.CreateElement("Docs");
- index_root.AppendChild(index_docs);
-
- XmlElement index_summary = index.CreateElement("summary");
- index_summary.InnerText = "To be added.";
- index_docs.AppendChild(index_summary);
-
- XmlElement index_remarks = index.CreateElement("remarks");
- index_remarks.InnerText = "To be added.";
- index_docs.AppendChild(index_remarks);
-
- WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
- writer => WriteXml (index.DocumentElement, writer));
- }
-
- public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
- {
- var index = CreateIndexForTypes (dest);
-
- var found = new HashSet<string> ();
- foreach (var assemblySet in this.assemblies) {
- using (assemblySet) {
- foreach (AssemblyDefinition assembly in assemblySet.Assemblies) {
- var frameworkEntry = frameworks.StartProcessingAssembly (assembly, assemblySet.Importers);
-
- foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames)) {
- var typeEntry = frameworkEntry.ProcessType (type);
-
- string relpath = DoUpdateType (type, typeEntry, basepath, dest);
- if (relpath == null)
- continue;
-
- found.Add (type.FullName);
-
- if (index == null)
- continue;
-
- index.Add (assembly);
- index.Add (type);
- }
- }
- }
- }
-
- if (index != null)
- index.Write ();
-
- if (ignore_missing_types)
- return;
-
- var notFound = from n in typenames where !found.Contains (n) select n;
- if (notFound.Any ())
- throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
- }
-
- class IndexForTypes {
-
- MDocUpdater app;
- string indexFile;
-
- XmlDocument index;
- XmlElement index_types;
- XmlElement index_assemblies;
-
- public IndexForTypes (MDocUpdater app, string indexFile, XmlDocument index)
- {
- this.app = app;
- this.indexFile = indexFile;
- this.index = index;
-
- index_types = WriteElement (index.DocumentElement, "Types");
- index_assemblies = WriteElement (index.DocumentElement, "Assemblies");
- }
-
- public void Add (AssemblyDefinition assembly)
- {
- if (index_assemblies.SelectSingleNode ("Assembly[@Name='" + assembly.Name.Name + "']") != null)
- return;
-
- app.AddIndexAssembly (assembly, index_assemblies);
- }
-
- public void Add (TypeDefinition type)
- {
- app.AddIndexType (type, index_types);
- }
-
- public void Write ()
- {
- SortIndexEntries (index_types);
- WriteFile (indexFile, FileMode.Create,
- writer => WriteXml (index.DocumentElement, writer));
- }
- }
-
- IndexForTypes CreateIndexForTypes (string dest)
- {
- string indexFile = Path.Combine (dest, "index.xml");
- if (File.Exists (indexFile))
- return null;
- return new IndexForTypes (this, indexFile, CreateIndexStub ());
- }
-
- /// <summary>Constructs the presumed path to the type's documentation file</summary>
- /// <returns><c>true</c>, if the type file was found, <c>false</c> otherwise.</returns>
- /// <param name="result">A typle that contains 1) the 'reltypefile', 2) the 'typefile', and 3) the file info</param>
- bool TryFindTypeFile(string nsname, string typename, string basepath, out Tuple<string, string, FileInfo> result) {
- string reltypefile = DocUtils.PathCombine (nsname, typename + ".xml");
- string typefile = Path.Combine (basepath, reltypefile);
- System.IO.FileInfo file = new System.IO.FileInfo(typefile);
-
- result = new Tuple<string, string, FileInfo> (reltypefile, typefile, file);
-
- return file.Exists;
- }
-
- public string DoUpdateType (TypeDefinition type, FrameworkTypeEntry typeEntry, string basepath, string dest)
- {
- if (type.Namespace == null)
- Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
- type.FullName);
- if (!IsPublic (type))
- return null;
-
- // Must get the A+B form of the type name.
- string typename = GetTypeFileName(type);
- string nsname = DocUtils.GetNamespace (type);
-
- // Find the file, if it exists
- string[] searchLocations = new string[] {
- nsname
- };
-
- if (MDocUpdater.HasDroppedNamespace (type)) {
- // If dropping namespace, types may have moved into a couple of different places.
- var newSearchLocations = searchLocations.Union (new string[] {
- string.Format ("{0}.{1}", droppedNamespace, nsname),
- nsname.Replace (droppedNamespace + ".", string.Empty),
- MDocUpdater.droppedNamespace
- });
-
- searchLocations = newSearchLocations.ToArray ();
- }
-
- string reltypefile="", typefile="";
- System.IO.FileInfo file = null;
-
- foreach (var f in searchLocations) {
- Tuple<string, string, FileInfo> result;
- bool fileExists = TryFindTypeFile (f, typename, basepath, out result);
-
- if (fileExists) {
- reltypefile = result.Item1;
- typefile = result.Item2;
- file = result.Item3;
-
- break;
- }
- }
-
- if (file == null || !file.Exists) {
- // we were not able to find a file, let's use the original type informatio.
- // so that we create the stub in the right place.
- Tuple<string, string, FileInfo> result;
- TryFindTypeFile (nsname, typename, basepath, out result);
-
- reltypefile = result.Item1;
- typefile = result.Item2;
- file = result.Item3;
- }
-
- string output = null;
- if (dest == null) {
- output = typefile;
- } else if (dest == "-") {
- output = null;
- } else {
- output = Path.Combine (dest, reltypefile);
- }
-
- if (file != null && file.Exists) {
- // Update
- XmlDocument basefile = new XmlDocument();
- try {
- basefile.Load(typefile);
- } catch (Exception e) {
- throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
- }
-
- DoUpdateType2("Updating", basefile, type, typeEntry, output, false);
- } else {
- // Stub
- XmlElement td = StubType(type, output, typeEntry.Framework.Importers);
- if (td == null)
- return null;
- }
- return reltypefile;
- }
-
- private static string GetTypeFileName (TypeReference type)
- {
- return filenameFormatter.GetName (type);
- }
-
- public static string GetTypeFileName (string typename)
- {
- StringBuilder filename = new StringBuilder (typename.Length);
- int numArgs = 0;
- int numLt = 0;
- bool copy = true;
- for (int i = 0; i < typename.Length; ++i) {
- char c = typename [i];
- switch (c) {
- case '<':
- copy = false;
- ++numLt;
- break;
- case '>':
- --numLt;
- if (numLt == 0) {
- filename.Append ('`').Append ((numArgs+1).ToString());
- numArgs = 0;
- copy = true;
- }
- break;
- case ',':
- if (numLt == 1)
- ++numArgs;
- break;
- default:
- if (copy)
- filename.Append (c);
- break;
- }
- }
- return filename.ToString ();
- }
-
- private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
- {
- XmlElement index_assembly = null;
- if (IsMultiAssembly)
- index_assembly = (XmlElement)parent.SelectSingleNode ("Assembly[@Name='"+ assembly.Name.Name +"']");
-
- if (index_assembly == null)
- index_assembly = parent.OwnerDocument.CreateElement ("Assembly");
-
- index_assembly.SetAttribute ("Name", assembly.Name.Name);
- index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
-
- AssemblyNameDefinition name = assembly.Name;
- if (name.HasPublicKey) {
- XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
- var key = new StringBuilder (name.PublicKey.Length*3 + 2);
- key.Append ("[");
- foreach (byte b in name.PublicKey)
- key.AppendFormat ("{0,2:x2} ", b);
- key.Append ("]");
- pubkey.InnerText = key.ToString ();
- index_assembly.AppendChild (pubkey);
- }
-
- if (!string.IsNullOrEmpty (name.Culture)) {
- XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
- culture.InnerText = name.Culture;
- index_assembly.AppendChild (culture);
- }
-
- MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
- parent.AppendChild(index_assembly);
- }
-
- private void AddIndexType (TypeDefinition type, XmlElement index_types)
- {
- string typename = GetTypeFileName(type);
-
- // Add namespace and type nodes into the index file as needed
- string ns = DocUtils.GetNamespace (type);
- XmlElement nsnode = (XmlElement) index_types.SelectSingleNode ("Namespace[@Name='" + ns + "']");
- if (nsnode == null) {
- nsnode = index_types.OwnerDocument.CreateElement("Namespace");
- nsnode.SetAttribute ("Name", ns);
- index_types.AppendChild (nsnode);
- }
- string doc_typename = GetDocTypeName (type);
- XmlElement typenode = (XmlElement) nsnode.SelectSingleNode ("Type[@Name='" + typename + "']");
- if (typenode == null) {
- typenode = index_types.OwnerDocument.CreateElement ("Type");
- typenode.SetAttribute ("Name", typename);
- nsnode.AppendChild (typenode);
- }
- if (typename != doc_typename)
- typenode.SetAttribute("DisplayName", doc_typename);
- else
- typenode.RemoveAttribute("DisplayName");
-
- typenode.SetAttribute ("Kind", GetTypeKind (type));
- }
-
- private void DoUpdateAssemblies (string source, string dest)
- {
- string indexfile = dest + "/index.xml";
- XmlDocument index;
- if (System.IO.File.Exists(indexfile)) {
- index = new XmlDocument();
- index.Load(indexfile);
-
- // Format change
- ClearElement(index.DocumentElement, "Assembly");
- ClearElement(index.DocumentElement, "Attributes");
- } else {
- index = CreateIndexStub();
- }
-
- XmlElement index_types = WriteElement(index.DocumentElement, "Types");
- XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
- if (!IsMultiAssembly)
- index_assemblies.RemoveAll ();
-
-
- HashSet<string> goodfiles = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
-
- int processedAssemblyCount = 0;
- foreach (var assemblySet in assemblies) {
- using (assemblySet) {
- foreach (AssemblyDefinition assm in assemblySet.Assemblies) {
- AddIndexAssembly (assm, index_assemblies);
- DoUpdateAssembly (assemblySet, assm, index_types, source, dest, goodfiles);
- processedAssemblyCount++;
- }
- }
- }
-
- string defaultTitle = "Untitled";
- if (processedAssemblyCount == 1)
- defaultTitle = assemblies[0].Assemblies.First ().Name.Name;
- WriteElementInitialText (index.DocumentElement, "Title", defaultTitle);
-
- SortIndexEntries (index_types);
-
- CleanupFiles (dest, goodfiles);
- CleanupIndexTypes (index_types, goodfiles);
- CleanupExtensions (index_types);
-
- WriteFile (indexfile, FileMode.Create,
- writer => WriteXml(index.DocumentElement, writer));
- }
-
- private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
-
- private void DoUpdateAssembly (AssemblySet assemblySet, AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
- {
- var frameworkEntry = frameworks.StartProcessingAssembly (assembly, assemblySet.Importers);
-
- foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
- string typename = GetTypeFileName(type);
- if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0)
- continue;
-
- var typeEntry = frameworkEntry.ProcessType (type);
-
- string reltypepath = DoUpdateType (type, typeEntry, source, dest);
- if (reltypepath == null)
- continue;
-
- // Add namespace and type nodes into the index file as needed
- AddIndexType (type, index_types);
-
- // Ensure the namespace index file exists
- string namespaceToUse = type.Namespace;
- if (HasDroppedNamespace(assembly)) {
- namespaceToUse = string.Format ("{0}.{1}", droppedNamespace, namespaceToUse);
- }
- string onsdoc = DocUtils.PathCombine (dest, namespaceToUse + ".xml");
- string nsdoc = DocUtils.PathCombine (dest, "ns-" + namespaceToUse + ".xml");
- if (File.Exists (onsdoc)) {
- File.Move (onsdoc, nsdoc);
- }
-
- if (!File.Exists (nsdoc)) {
- Console.WriteLine("New Namespace File: " + type.Namespace);
- WriteNamespaceStub(namespaceToUse, dest);
- }
-
- goodfiles.Add (reltypepath);
- }
- }
-
- private static void SortIndexEntries (XmlElement indexTypes)
- {
- XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
- XmlNodeComparer c = new AttributeNameComparer ();
- SortXmlNodes (indexTypes, namespaces, c);
-
- for (int i = 0; i < namespaces.Count; ++i)
- SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
- }
-
- private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
- {
- MyXmlNodeList l = new MyXmlNodeList (children.Count);
- for (int i = 0; i < children.Count; ++i)
- l.Add (children [i]);
- l.Sort (comparer);
- for (int i = l.Count - 1; i > 0; --i) {
- parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
- }
- }
-
- abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
- {
- public abstract int Compare (XmlNode x, XmlNode y);
-
- public int Compare (object x, object y)
- {
- return Compare ((XmlNode) x, (XmlNode) y);
- }
- }
-
- class AttributeNameComparer : XmlNodeComparer {
- string attribute;
-
- public AttributeNameComparer ()
- : this ("Name")
- {
- }
-
- public AttributeNameComparer (string attribute)
- {
- this.attribute = attribute;
- }
-
- public override int Compare (XmlNode x, XmlNode y)
- {
- return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
- }
- }
-
- class VersionComparer : XmlNodeComparer {
- public override int Compare (XmlNode x, XmlNode y)
- {
- // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
- string a = GetVersion (x.InnerText);
- string b = GetVersion (y.InnerText);
- return new Version (a).CompareTo (new Version (b));
- }
-
- static string GetVersion (string v)
- {
- int n = v.IndexOf ("x");
- if (n < 0)
- return v;
- return v.Substring (0, n-1);
- }
- }
-
- private static string GetTypeKind (TypeDefinition type)
- {
- if (type.IsEnum)
- return "Enumeration";
- if (type.IsValueType)
- return "Structure";
- if (type.IsInterface)
- return "Interface";
- if (DocUtils.IsDelegate (type))
- return "Delegate";
- if (type.IsClass || type.FullName == "System.Enum") // FIXME
- return "Class";
- throw new ArgumentException ("Unknown kind for type: " + type.FullName);
- }
-
- public static bool IsPublic (TypeDefinition type)
- {
- TypeDefinition decl = type;
- while (decl != null) {
- if (!(decl.IsPublic || decl.IsNestedPublic ||
- decl.IsNestedFamily || decl.IsNestedFamily || decl.IsNestedFamilyOrAssembly)) {
- return false;
- }
- decl = (TypeDefinition) decl.DeclaringType;
- }
- return true;
- }
-
- private void CleanupFiles (string dest, HashSet<string> goodfiles)
- {
- // Look for files that no longer correspond to types
- foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*").Where(d => Path.GetFileName(d.FullName) != "FrameworksIndex")) {
- foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
- string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
- if (!goodfiles.Contains (relTypeFile)) {
- XmlDocument doc = new XmlDocument ();
- doc.Load (typefile.FullName);
- XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
- var assemblyNameNode = doc.SelectSingleNode ("/Type/AssemblyInfo/AssemblyName");
- if (assemblyNameNode == null){
- Warning ("Did not find /Type/AssemblyInfo/AssemblyName on {0}", typefile.FullName);
- continue;
- }
- string assemblyName = assemblyNameNode.InnerText;
-
-
- Action saveDoc = () => {
- using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
- WriteXml(doc.DocumentElement, writer);
- };
-
- if (!IsMultiAssembly) { // only do this for "regular" runs
- AssemblyDefinition assembly = assemblies
- .SelectMany (aset => aset.Assemblies)
- .FirstOrDefault (a => a.Name.Name == assemblyName);
- if (e != null && !no_assembly_versions && assembly != null && assemblyName != null && UpdateAssemblyVersions (e, assembly, GetAssemblyVersions (assemblyName), false)) {
- saveDoc ();
- goodfiles.Add (relTypeFile);
- continue;
- }
- }
-
- Action actuallyDelete = () => {
- string newname = typefile.FullName + ".remove";
- try { System.IO.File.Delete (newname); } catch (Exception) { Warning ("Unable to delete existing file: {0}", newname); }
- try { typefile.MoveTo (newname); } catch (Exception) { Warning ("Unable to rename to: {0}", newname); }
- Console.WriteLine ("Class no longer present; file renamed: " + Path.Combine (nsdir.Name, typefile.Name));
- };
-
- if (string.IsNullOrWhiteSpace (PreserveTag)) { // only do this if there was not a -preserve
- saveDoc ();
-
- var unifiedAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[@apistyle='unified']");
- var classicAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[not(@apistyle) or @apistyle='classic']");
- var unifiedMembers = doc.SelectNodes ("//Member[@apistyle='unified']|//Member/AssemblyInfo[@apistyle='unified']");
- var classicMembers = doc.SelectNodes ("//Member[@apistyle='classic']|//Member/AssemblyInfo[@apistyle='classic']");
- bool isUnifiedRun = HasDroppedAnyNamespace ();
- bool isClassicOrNormalRun = !isUnifiedRun;
-
- Action<XmlNode, ApiStyle> removeStyles = (x, style) => {
- var styledNodes = doc.SelectNodes("//*[@apistyle='"+ style.ToString ().ToLowerInvariant () +"']");
- if (styledNodes != null && styledNodes.Count > 0) {
- foreach(var node in styledNodes.Cast<XmlNode> ()) {
- node.ParentNode.RemoveChild (node);
- }
- }
- saveDoc ();
- };
- if (isClassicOrNormalRun) {
- if (unifiedAssemblyNode != null || unifiedMembers.Count > 0) {
- Warning ("*** this type is marked as unified, not deleting during this run: {0}", typefile.FullName);
- // if truly removed from both assemblies, it will be removed fully during the unified run
- removeStyles (doc, ApiStyle.Classic);
- continue;
- } else {
- // we should be safe to delete here because it was not marked as a unified assembly
- actuallyDelete ();
- }
- }
- if (isUnifiedRun) {
- if (classicAssemblyNode != null || classicMembers.Count > 0) {
- Warning ("*** this type is marked as classic, not deleting {0}", typefile.FullName);
- continue;
- } else {
- // safe to delete because it wasn't marked as a classic assembly, so the type is gone in both.
- actuallyDelete ();
- }
- }
- }
- }
- }
- }
- }
-
- private static TextWriter OpenWrite (string path, FileMode mode)
- {
- var w = new StreamWriter (
- new FileStream (path, mode),
- new UTF8Encoding (false)
- );
- w.NewLine = "\n";
- return w;
- }
-
- private string[] GetAssemblyVersions (string assemblyName)
- {
- return (from a in assemblies.SelectMany(aset => aset.Assemblies)
- where a.Name.Name == assemblyName
- select GetAssemblyVersion (a)).ToArray ();
- }
-
- private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
- {
- // Look for type nodes that no longer correspond to types
- MyXmlNodeList remove = new MyXmlNodeList ();
- foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
- string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
- if (!goodfiles.Contains (fulltypename)) {
- remove.Add (typenode);
- }
- }
- foreach (XmlNode n in remove)
- n.ParentNode.RemoveChild (n);
- }
-
- private void CleanupExtensions (XmlElement index_types)
- {
- XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
- if (extensionMethods.Count == 0) {
- if (e == null)
- return;
- index_types.SelectSingleNode ("/Overview").RemoveChild (e);
- return;
- }
- if (e == null) {
- e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
- index_types.SelectSingleNode ("/Overview").AppendChild (e);
- }
- else
- e.RemoveAll ();
- extensionMethods.Sort (DefaultExtensionMethodComparer);
- foreach (XmlNode m in extensionMethods) {
- e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
- }
- }
-
- class ExtensionMethodComparer : XmlNodeComparer {
- public override int Compare (XmlNode x, XmlNode y)
- {
- XmlNode xLink = x.SelectSingleNode ("Member/Link");
- XmlNode yLink = y.SelectSingleNode ("Member/Link");
-
- int n = xLink.Attributes ["Type"].Value.CompareTo (
- yLink.Attributes ["Type"].Value);
- if (n != 0)
- return n;
- n = xLink.Attributes ["Member"].Value.CompareTo (
- yLink.Attributes ["Member"].Value);
- if (n == 0 && !object.ReferenceEquals (x, y))
- throw new InvalidOperationException ("Duplicate extension method found!");
- return n;
- }
- }
-
- static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
-
- public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, FrameworkTypeEntry typeEntry, string output, bool insertSince)
- {
- Console.WriteLine(message + ": " + type.FullName);
-
- StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
-
- // Update type metadata
- UpdateType(basefile.DocumentElement, type, typeEntry);
-
- // Update existing members. Delete member nodes that no longer should be there,
- // and remember what members are already documented so we don't add them again.
-
- MyXmlNodeList todelete = new MyXmlNodeList ();
-
- foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
- XmlElement oldmember = info.Node;
- MemberReference oldmember2 = info.Member;
-
- if (info.Member != null && info.Node != null) {
- // Check for an error condition where the xml MemberName doesn't match the matched member
- var memberName = GetMemberName (info.Member);
- var memberAttribute = info.Node.Attributes ["MemberName"];
- if (memberAttribute == null || (memberAttribute.Value != memberName && memberAttribute.Value.Split (',').Length != memberName.Split (',').Length)) {
- oldmember.SetAttribute ("MemberName", memberName);
- }
- }
-
- string sig = oldmember2 != null ? memberFormatters [1].GetDeclaration (oldmember2) : null;
-
- // Interface implementations and overrides are deleted from the docs
- // unless the overrides option is given.
- if (oldmember2 != null && sig == null)
- oldmember2 = null;
-
- // Deleted (or signature changed)
- if (oldmember2 == null) {
- if (!string.IsNullOrWhiteSpace (FrameworksPath)) {
- // verify that this member wasn't seen in another framework and is indeed valid
- var sigFromXml = oldmember
- .GetElementsByTagName ("MemberSignature")
- .Cast<XmlElement> ()
- .FirstOrDefault (x => x.GetAttribute ("Language").Equals ("ILAsm"));
-
- if (sigFromXml != null) {
- var sigvalue = sigFromXml.GetAttribute ("Value");
- Func<FrameworkEntry, bool> findTypes = fx => {
- var tInstance = fx.Types.FirstOrDefault(t => t.Equals(typeEntry));
- if (tInstance != null) {
- return tInstance.ContainsCSharpSig(sigvalue);
- }
- return false;
- };
- if (frameworksCache.Frameworks.Any (findTypes))
- continue;
- }
- }
-
- if (!no_assembly_versions && UpdateAssemblyVersions (oldmember, type.Module.Assembly, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
- continue;
-
- DeleteMember ("Member Removed", output, oldmember, todelete, type);
- continue;
- }
-
- // Duplicated
- if (seenmembers.ContainsKey (sig)) {
- if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
- // ignore, already seen
- }
- else
- DeleteMember ("Duplicate Member Found", output, oldmember, todelete, type);
-
- continue;
- }
-
- // Update signature information
- UpdateMember(info, typeEntry);
-
- // get all apistyles of sig from info.Node
- var styles = oldmember.GetElementsByTagName ("MemberSignature").Cast<XmlElement> ()
- .Where (x => x.GetAttribute ("Language") == "ILAsm" && !seenmembers.ContainsKey(x.GetAttribute("Value")))
- .Select (x => x.GetAttribute ("Value"));
-
-
- typeEntry.ProcessMember (info.Member);
-
- foreach (var stylesig in styles) {
- seenmembers.Add (stylesig, oldmember);
- }
- }
- foreach (XmlElement oldmember in todelete)
- oldmember.ParentNode.RemoveChild (oldmember);
-
-
- if (!DocUtils.IsDelegate (type)) {
- XmlNode members = WriteElement (basefile.DocumentElement, "Members");
- var typemembers = type.GetMembers()
- .Where(m => {
- if (m is TypeDefinition) return false;
- string cssig = memberFormatters [0].GetDeclaration (m);
- if (cssig == null) return false;
- string sig = memberFormatters [1].GetDeclaration (m);
- if (seenmembers.ContainsKey(sig)) return false;
-
- // Verify that the member isn't an explicitly implemented
- // member of an internal interface, in which case we shouldn't return true.
- MethodDefinition methdef = null;
- if (m is MethodDefinition)
- methdef = m as MethodDefinition;
- else if (m is PropertyDefinition) {
- var prop = m as PropertyDefinition;
- methdef = prop.GetMethod ?? prop.SetMethod;
- }
-
- if (methdef != null) {
- TypeReference iface;
- MethodReference imethod;
-
- if (methdef.Overrides.Count == 1 && !methdef.IsPublic) {
- DocUtils.GetInfoForExplicitlyImplementedMethod (methdef, out iface, out imethod);
- if (!IsPublic (iface.Resolve ())) return false;
- }
- }
-
- return true;
- })
- .ToArray();
- foreach (MemberReference m in typemembers) {
- XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m), members, typeEntry);
- if (mm == null) continue;
-
- if (MDocUpdater.SwitchingToMagicTypes || MDocUpdater.HasDroppedNamespace (m)) {
- // this is a unified style API that obviously doesn't exist in the classic API. Let's mark
- // it with apistyle="unified", so that it's not displayed for classic style APIs
- mm.AddApiStyle (ApiStyle.Unified);
- }
-
- Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
- additions++;
- }
- }
-
- // Import code snippets from files
- foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
- if (!(code is XmlElement)) continue;
- string file = ((XmlElement)code).GetAttribute("src");
- string lang = ((XmlElement)code).GetAttribute("lang");
- if (file != "") {
- string src = GetCodeSource (lang, Path.Combine (srcPath, file));
- if (src != null)
- code.InnerText = src;
- }
- }
-
- if (insertSince && since != null) {
- XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
- docs.AppendChild (CreateSinceNode (basefile));
- }
-
- do {
- XmlElement d = basefile.DocumentElement ["Docs"];
- XmlElement m = basefile.DocumentElement ["Members"];
- if (d != null && m != null)
- basefile.DocumentElement.InsertBefore (
- basefile.DocumentElement.RemoveChild (d), m);
- SortTypeMembers (m);
- } while (false);
-
- if (output == null)
- WriteXml(basefile.DocumentElement, Console.Out);
- else {
- FileInfo file = new FileInfo (output);
- if (!file.Directory.Exists) {
- Console.WriteLine("Namespace Directory Created: " + type.Namespace);
- file.Directory.Create ();
- }
- WriteFile (output, FileMode.Create,
- writer => WriteXml(basefile.DocumentElement, writer));
- }
- }
-
- private string GetCodeSource (string lang, string file)
- {
- int anchorStart;
- if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
- // Grab the specified region
- string region = "#region " + file.Substring (anchorStart + 4);
- file = file.Substring (0, anchorStart + 3);
- try {
- using (StreamReader reader = new StreamReader (file)) {
- string line;
- StringBuilder src = new StringBuilder ();
- int indent = -1;
- while ((line = reader.ReadLine ()) != null) {
- if (line.Trim() == region) {
- indent = line.IndexOf (region);
- continue;
- }
- if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
- break;
- }
- if (indent >= 0)
- src.Append (
- (line.Length > 0 ? line.Substring (indent) : string.Empty) +
- "\n");
- }
- return src.ToString ();
- }
- } catch (Exception e) {
- Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
- file, region, show_exceptions ? e.ToString () : e.Message);
- return null;
- }
- }
- try {
- using (StreamReader reader = new StreamReader (file))
- return reader.ReadToEnd ();
- } catch (Exception e) {
- Warning ("Could not load <code/> file '" + file + "': " + e.Message);
- }
- return null;
- }
-
- void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete, TypeDefinition type)
- {
- string format = output != null
- ? "{0}: File='{1}'; Signature='{4}'"
- : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
- string signature = member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value;
- Warning (format,
- reason,
- output,
- member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
- member.Attributes ["MemberName"].Value,
- signature);
-
- // Identify all of the different states that could affect our decision to delete the member
- bool shouldPreserve = !string.IsNullOrWhiteSpace (PreserveTag);
- bool hasContent = MemberDocsHaveUserContent (member);
- bool shouldDelete = !shouldPreserve && (delete || !hasContent);
-
- bool unifiedRun = HasDroppedNamespace (type);
-
- var classicAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[not(@apistyle) or @apistyle='classic']");
- bool nodeIsClassic = classicAssemblyInfo != null || member.HasApiStyle (ApiStyle.Classic);
- var unifiedAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']");
- bool nodeIsUnified = unifiedAssemblyInfo != null || member.HasApiStyle (ApiStyle.Unified);
-
- Action actuallyDelete = () => {
- todelete.Add (member);
- deletions++;
- };
-
- if (!shouldDelete) {
- // explicitly not deleting
- string message = shouldPreserve ?
- "Not deleting '{0}' due to --preserve." :
- "Not deleting '{0}'; must be enabled with the --delete option";
- Warning (message, signature);
- } else if (unifiedRun && nodeIsClassic) {
- // this is a unified run, and the member doesn't exist, but is marked as being in the classic assembly.
- member.RemoveApiStyle (ApiStyle.Unified);
- member.AddApiStyle (ApiStyle.Classic);
- Warning ("Not removing '{0}' since it's still in the classic assembly.", signature);
- } else if (unifiedRun && !nodeIsClassic) {
- // unified run, and the node is not classic, which means it doesn't exist anywhere.
- actuallyDelete ();
- } else {
- if (!isClassicRun || (isClassicRun && !nodeIsClassic && !nodeIsUnified)) { // regular codepath (ie. not classic/unified)
- actuallyDelete ();
- } else { // this is a classic run
- Warning ("Removing classic from '{0}' ... will be removed in the unified run if not present there.", signature);
- member.RemoveApiStyle (ApiStyle.Classic);
- if (classicAssemblyInfo != null) {
- member.RemoveChild (classicAssemblyInfo);
- }
- }
- }
- }
-
- class MemberComparer : XmlNodeComparer {
- public override int Compare (XmlNode x, XmlNode y)
- {
- int r;
- string xMemberName = x.Attributes ["MemberName"].Value;
- string yMemberName = y.Attributes ["MemberName"].Value;
-
- // generic methods *end* with '>'
- // it's possible for explicitly implemented generic interfaces to
- // contain <...> without being a generic method
- if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
- (r = xMemberName.CompareTo (yMemberName)) != 0)
- return r;
-
- int lt;
- if ((lt = xMemberName.IndexOf ("<")) >= 0)
- xMemberName = xMemberName.Substring (0, lt);
- if ((lt = yMemberName.IndexOf ("<")) >= 0)
- yMemberName = yMemberName.Substring (0, lt);
- if ((r = xMemberName.CompareTo (yMemberName)) != 0)
- return r;
-
- // Handle MemberGroup sorting
- var sc = StringComparison.InvariantCultureIgnoreCase;
- if (x.Name.Equals ("MemberGroup", sc) || y.Name.Equals ("MemberGroup", sc)) {
- if (x.Name.Equals ("MemberGroup", sc) && y.Name.Equals ("Member", sc))
- return -1;
- else if (x.Name.Equals ("Member", sc) && y.Name.Equals ("MemberGroup", sc))
- return 1;
- else
- return xMemberName.CompareTo (yMemberName);
- }
-
- // if @MemberName matches, then it's either two different types of
- // members sharing the same name, e.g. field & property, or it's an
- // overloaded method.
- // for different type, sort based on MemberType value.
- r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
- y.SelectSingleNode ("MemberType").InnerText);
- if (r != 0)
- return r;
-
- // same type -- must be an overloaded method. Sort based on type
- // parameter count, then parameter count, then by the parameter
- // type names.
- XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
- XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
- if (xTypeParams.Count != yTypeParams.Count)
- return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
- for (int i = 0; i < xTypeParams.Count; ++i) {
- r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
- yTypeParams [i].Attributes ["Name"].Value);
- if (r != 0)
- return r;
- }
-
- XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
- XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
- if (xParams.Count != yParams.Count)
- return xParams.Count <= yParams.Count ? -1 : 1;
- for (int i = 0; i < xParams.Count; ++i) {
- r = xParams [i].Attributes ["Type"].Value.CompareTo (
- yParams [i].Attributes ["Type"].Value);
- if (r != 0)
- return r;
- }
- // all parameters match, but return value might not match if it was
- // changed between one version and another.
- XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
- XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
- if (xReturn != null && yReturn != null) {
- r = xReturn.InnerText.CompareTo (yReturn.InnerText);
- if (r != 0)
- return r;
- }
-
- return 0;
- }
- }
-
- static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
-
- private static void SortTypeMembers (XmlNode members)
- {
- if (members == null)
- return;
- SortXmlNodes (members, members.SelectNodes ("Member|MemberGroup"), DefaultMemberComparer);
- }
-
- private static bool MemberDocsHaveUserContent (XmlNode e)
- {
- e = (XmlElement)e.SelectSingleNode("Docs");
- if (e == null) return false;
- foreach (XmlElement d in e.SelectNodes("*"))
- if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
- return true;
- return false;
- }
-
- // UPDATE HELPER FUNCTIONS
-
- // CREATE A STUB DOCUMENTATION FILE
-
- public XmlElement StubType (TypeDefinition type, string output, IEnumerable<DocumentationImporter> importers)
- {
- string typesig = typeFormatters [0].GetDeclaration (type);
- if (typesig == null) return null; // not publicly visible
-
- XmlDocument doc = new XmlDocument();
- XmlElement root = doc.CreateElement("Type");
- doc.AppendChild (root);
-
- var frameworkEntry = frameworks.StartProcessingAssembly (type.Module.Assembly, importers);
- var typeEntry = frameworkEntry.ProcessType (type);
- DoUpdateType2 ("New Type", doc, type, typeEntry, output, true);
-
- return root;
- }
-
- private XmlElement CreateSinceNode (XmlDocument doc)
- {
- XmlElement s = doc.CreateElement ("since");
- s.SetAttribute ("version", since);
- return s;
- }
-
- // STUBBING/UPDATING FUNCTIONS
-
- public void UpdateType (XmlElement root, TypeDefinition type, FrameworkTypeEntry typeEntry)
- {
- root.SetAttribute("Name", GetDocTypeName (type));
- root.SetAttribute("FullName", GetDocTypeFullName (type));
-
- foreach (MemberFormatter f in typeFormatters) {
- string element = "TypeSignature[@Language='" + f.Language + "']";
- string valueToUse = f.GetDeclaration (type);
-
- AddXmlNode (
- root.SelectNodes (element).Cast<XmlElement> ().ToArray (),
- x => x.GetAttribute ("Value") == valueToUse,
- x => x.SetAttribute ("Value", valueToUse),
- () => {
- var node = WriteElementAttribute (root, element, "Language", f.Language, forceNewElement: true);
- var newnode = WriteElementAttribute (root, node, "Value", valueToUse);
- return newnode;
- },
- type);
- }
-
- AddAssemblyNameToNode (root, type);
-
- string assemblyInfoNodeFilter = MDocUpdater.HasDroppedNamespace (type) ? "[@apistyle='unified']" : "[not(@apistyle) or @apistyle='classic']";
- Func<XmlElement, bool> assemblyFilter = x => x.SelectSingleNode ("AssemblyName").InnerText == type.Module.Assembly.Name.Name;
- foreach(var ass in root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ().Where (assemblyFilter))
- {
- WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
- if (!no_assembly_versions) {
- UpdateAssemblyVersions (ass, type, true);
- }
- else {
- var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
- foreach (var version in versions)
- ass.RemoveChild (version);
- }
- if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
- WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
- else
- ClearElement(ass, "AssemblyCulture");
-
-
- // Why-oh-why do we put assembly attributes in each type file?
- // Neither monodoc nor monodocs2html use them, so I'm deleting them
- // since they're outdated in current docs, and a waste of space.
- //MakeAttributes(ass, type.Assembly, true);
- XmlNode assattrs = ass.SelectSingleNode("Attributes");
- if (assattrs != null)
- ass.RemoveChild(assattrs);
-
- NormalizeWhitespace(ass);
- }
-
- if (type.IsGenericType ()) {
- MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
- } else {
- ClearElement(root, "TypeParameters");
- }
-
- if (type.BaseType != null) {
- XmlElement basenode = WriteElement(root, "Base");
-
- string basetypename = GetDocTypeFullName (type.BaseType);
- if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
-
- if (string.IsNullOrWhiteSpace (FrameworksPath))
- WriteElementText (root, "Base/BaseTypeName", basetypename);
- else {
- // Check for the possibility of an alternate inheritance chain in different frameworks
- var typeElements = basenode.GetElementsByTagName ("BaseTypeName");
-
- if (typeElements.Count == 0) // no existing elements, just add
- WriteElementText (root, "Base/BaseTypeName", basetypename);
- else {
- // There's already a BaseTypeName, see if it matches
- if (typeElements[0].InnerText != basetypename) {
- // Add a framework alternate if one doesn't already exist
- var existing = typeElements.Cast<XmlNode> ().Where (n => n.InnerText == basetypename);
- if (!existing.Any ()) {
- var newNode = WriteElementText (basenode, "BaseTypeName", basetypename, forceNewElement:true);
- WriteElementAttribute (basenode, newNode, "FrameworkAlternate", typeEntry.Framework.Name);
- }
- }
- }
- }
-
- // Document how this type instantiates the generic parameters of its base type
- TypeReference origBase = type.BaseType.GetElementType ();
- if (origBase.IsGenericType ()) {
- ClearElement(basenode, "BaseTypeArguments");
- GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
- IList<TypeReference> baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
- IList<GenericParameter> baseGenParams = origBase.GenericParameters;
- if (baseGenArgs.Count != baseGenParams.Count)
- throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
- for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
- GenericParameter param = baseGenParams [i];
- TypeReference value = baseGenArgs [i];
-
- XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
- XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
- bta.AppendChild(arg);
- arg.SetAttribute ("TypeParamName", param.Name);
- arg.InnerText = GetDocTypeFullName (value);
- }
- }
- } else {
- ClearElement(root, "Base");
- }
-
- if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
- IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
- List<string> interface_names = userInterfaces
- .Select (iface => GetDocTypeFullName (iface))
- .OrderBy (s => s)
- .ToList ();
-
- XmlElement interfaces = WriteElement(root, "Interfaces");
- interfaces.RemoveAll();
- foreach (string iname in interface_names) {
- XmlElement iface = root.OwnerDocument.CreateElement("Interface");
- interfaces.AppendChild(iface);
- WriteElementText(iface, "InterfaceName", iname);
- }
- } else {
- ClearElement(root, "Interfaces");
- }
-
- MakeAttributes (root, GetCustomAttributes (type), type);
-
- if (DocUtils.IsDelegate (type)) {
- MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
- var member = type.GetMethod ("Invoke");
- MakeParameters(root, member, member.Parameters);
- MakeReturnValue(root, member);
- }
-
- DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
- MakeDocNode (typeInfo, typeEntry.Framework.Importers);
-
- if (!DocUtils.IsDelegate (type))
- WriteElement (root, "Members");
-
- OrderTypeNodes (root, root.ChildNodes);
- NormalizeWhitespace(root);
- }
-
- /// <summary>Adds an AssemblyInfo with AssemblyName node to an XmlElement.</summary>
- /// <returns>The assembly that was either added, or was already present</returns>
- XmlElement AddAssemblyNameToNode (XmlElement root, TypeDefinition type)
- {
- return AddAssemblyNameToNode (root, type.Module);
- }
-
- /// <summary>Adds an AssemblyInfo with AssemblyName node to an XmlElement.</summary>
- /// <returns>The assembly that was either added, or was already present</returns>
- XmlElement AddAssemblyNameToNode (XmlElement root, ModuleDefinition module)
- {
- Func<XmlElement, bool> assemblyFilter = x => {
- var existingName = x.SelectSingleNode ("AssemblyName");
-
- bool apiStyleMatches = true;
- string currentApiStyle = x.GetAttribute ("apistyle");
- if ((HasDroppedNamespace (module) && !string.IsNullOrWhiteSpace (currentApiStyle) && currentApiStyle != "unified") ||
- (isClassicRun && (string.IsNullOrWhiteSpace (currentApiStyle) || currentApiStyle != "classic"))) {
- apiStyleMatches = false;
- }
- return apiStyleMatches && (existingName == null || (existingName != null && existingName.InnerText == module.Assembly.Name.Name));
- };
-
- return AddAssemblyXmlNode (
- root.SelectNodes ("AssemblyInfo").Cast<XmlElement> ().ToArray (),
- assemblyFilter, x => WriteElementText (x, "AssemblyName", module.Assembly.Name.Name),
- () => {
- XmlElement ass = WriteElement (root, "AssemblyInfo", forceNewElement: true);
-
- if (MDocUpdater.HasDroppedNamespace (module))
- ass.AddApiStyle (ApiStyle.Unified);
- if (isClassicRun)
- ass.AddApiStyle (ApiStyle.Classic);
- return ass;
- }, module);
- }
-
- static readonly string[] TypeNodeOrder = {
- "TypeSignature",
- "MemberOfLibrary",
- "AssemblyInfo",
- "ThreadingSafetyStatement",
- "ThreadSafetyStatement",
- "TypeParameters",
- "Base",
- "Interfaces",
- "Attributes",
- "Parameters",
- "ReturnValue",
- "Docs",
- "Members",
- "TypeExcluded",
- };
-
- static void OrderTypeNodes (XmlNode member, XmlNodeList children)
- {
- ReorderNodes (member, children, TypeNodeOrder);
- }
-
- internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
- {
- List<T> l = new List<T> (list);
- l.Sort ();
- return l;
- }
-
- private void UpdateMember (DocsNodeInfo info, FrameworkTypeEntry typeEntry)
- {
- XmlElement me = (XmlElement) info.Node;
- MemberReference mi = info.Member;
- typeEntry.ProcessMember (mi);
- foreach (MemberFormatter f in memberFormatters) {
- string element = "MemberSignature[@Language='" + f.Language + "']";
-
- var valueToUse = f.GetDeclaration (mi);
-
- AddXmlNode (
- me.SelectNodes (element).Cast<XmlElement> ().ToArray(),
- x => x.GetAttribute("Value") == valueToUse,
- x => x.SetAttribute ("Value", valueToUse),
- () => {
- var node = WriteElementAttribute (me, element, "Language", f.Language, forceNewElement:true);
- var newNode = WriteElementAttribute (me, node, "Value", valueToUse);
- return newNode;
- },
- mi);
- }
-
- WriteElementText(me, "MemberType", GetMemberType(mi));
-
- if (!no_assembly_versions) {
- if (!IsMultiAssembly)
- UpdateAssemblyVersions (me, mi, true);
- else {
- var node = AddAssemblyNameToNode (me, mi.Module);
-
- UpdateAssemblyVersionForAssemblyInfo (node, me, new[] { GetAssemblyVersion (mi.Module.Assembly) }, add: true);
- }
- }
- else {
- ClearElement (me, "AssemblyInfo");
- }
-
- MakeAttributes (me, GetCustomAttributes (mi), mi.DeclaringType);
-
- MakeReturnValue(me, mi, MDocUpdater.HasDroppedNamespace(mi));
- if (mi is MethodReference) {
- MethodReference mb = (MethodReference) mi;
- if (mb.IsGenericMethod ())
- MakeTypeParameters (me, mb.GenericParameters, mi, MDocUpdater.HasDroppedNamespace(mi));
- }
- MakeParameters(me, mi, MDocUpdater.HasDroppedNamespace(mi));
-
- string fieldValue;
- if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
- WriteElementText(me, "MemberValue", fieldValue);
-
- info.Node = WriteElement (me, "Docs");
- MakeDocNode (info, typeEntry.Framework.Importers);
- OrderMemberNodes (me, me.ChildNodes);
- UpdateExtensionMethods (me, info);
- }
-
- static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, MemberReference member) {
- AddXmlNode (relevant, valueMatches, setValue, makeNewNode, member.Module);
- }
-
- static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, TypeDefinition type) {
- AddXmlNode (relevant, valueMatches, setValue, makeNewNode, type.Module);
- }
-
- static XmlElement AddAssemblyXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, ModuleDefinition module)
- {
- bool isUnified = MDocUpdater.HasDroppedNamespace (module);
- XmlElement thisAssemblyNode = relevant.FirstOrDefault (valueMatches);
- if (thisAssemblyNode == null) {
- thisAssemblyNode = makeNewNode ();
- }
- setValue (thisAssemblyNode);
-
- if (isUnified) {
- thisAssemblyNode.AddApiStyle (ApiStyle.Unified);
-
- foreach (var otherNodes in relevant.Where (n => n != thisAssemblyNode && n.DoesNotHaveApiStyle (ApiStyle.Unified))) {
- otherNodes.AddApiStyle (ApiStyle.Classic);
- }
- }
- return thisAssemblyNode;
- }
-
- /// <summary>Adds an xml node, reusing the node if it's available</summary>
- /// <param name="relevant">The existing set of nodes</param>
- /// <param name="valueMatches">Checks to see if the node's value matches what you're trying to write.</param>
- /// <param name="setValue">Sets the node's value</param>
- /// <param name="makeNewNode">Creates a new node, if valueMatches returns false.</param>
- static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, ModuleDefinition module)
- {
- bool shouldDuplicate = MDocUpdater.HasDroppedNamespace (module);
- var styleToUse = shouldDuplicate ? ApiStyle.Unified : ApiStyle.Classic;
- var existing = relevant;
- bool done = false;
- bool addedOldApiStyle = false;
-
- if (shouldDuplicate) {
- existing = existing.Where (n => n.HasApiStyle (styleToUse)).ToArray ();
- foreach (var n in relevant.Where (n => n.DoesNotHaveApiStyle (styleToUse))) {
- if (valueMatches (n)) {
- done = true;
- }
- else {
- n.AddApiStyle (ApiStyle.Classic);
- addedOldApiStyle = true;
- }
- }
- }
- if (!done) {
- if (!existing.Any ()) {
- var newNode = makeNewNode ();
- if (shouldDuplicate && addedOldApiStyle) {
- newNode.AddApiStyle (ApiStyle.Unified);
- }
- }
- else {
- var itemToReuse = existing.First ();
- setValue (itemToReuse);
-
- if (shouldDuplicate && addedOldApiStyle) {
- itemToReuse.AddApiStyle (styleToUse);
- }
- }
- }
- }
-
- static readonly string[] MemberNodeOrder = {
- "MemberSignature",
- "MemberType",
- "AssemblyInfo",
- "Attributes",
- "ReturnValue",
- "TypeParameters",
- "Parameters",
- "MemberValue",
- "Docs",
- "Excluded",
- "ExcludedLibrary",
- "Link",
- };
-
- static void OrderMemberNodes (XmlNode member, XmlNodeList children)
- {
- ReorderNodes (member, children, MemberNodeOrder);
- }
-
- static void ReorderNodes (XmlNode node, XmlNodeList children, string[] ordering)
- {
- MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
- for (int i = 0; i < ordering.Length; ++i) {
- for (int j = 0; j < children.Count; ++j) {
- XmlNode c = children [j];
- if (c.Name == ordering [i]) {
- newChildren.Add (c);
- }
- }
- }
- if (newChildren.Count >= 0)
- node.PrependChild ((XmlNode) newChildren [0]);
- for (int i = 1; i < newChildren.Count; ++i) {
- XmlNode prev = (XmlNode) newChildren [i-1];
- XmlNode cur = (XmlNode) newChildren [i];
- node.RemoveChild (cur);
- node.InsertAfter (cur, prev);
- }
- }
-
- IEnumerable<string> GetCustomAttributes (MemberReference mi)
- {
- IEnumerable<string> attrs = Enumerable.Empty<string>();
-
- ICustomAttributeProvider p = mi as ICustomAttributeProvider;
- if (p != null)
- attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
-
- PropertyDefinition pd = mi as PropertyDefinition;
- if (pd != null) {
- if (pd.GetMethod != null)
- attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
- if (pd.SetMethod != null)
- attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
- }
-
- EventDefinition ed = mi as EventDefinition;
- if (ed != null) {
- if (ed.AddMethod != null)
- attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
- if (ed.RemoveMethod != null)
- attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
- }
-
- return attrs;
- }
-
- IEnumerable<string> GetCustomAttributes (IList<CustomAttribute> attributes, string prefix)
- {
- foreach (CustomAttribute attribute in attributes.OrderBy (ca => ca.AttributeType.FullName)) {
-
- TypeDefinition attrType = attribute.AttributeType as TypeDefinition;
- if (attrType != null && !IsPublic (attrType))
- continue;
- if (slashdocFormatter.GetName (attribute.AttributeType) == null)
- continue;
-
- if (Array.IndexOf (IgnorableAttributes, attribute.AttributeType.FullName) >= 0)
- continue;
-
- StringList fields = new StringList ();
-
- for (int i = 0; i < attribute.ConstructorArguments.Count; ++i) {
- CustomAttributeArgument argument = attribute.ConstructorArguments [i];
- fields.Add (MakeAttributesValueString (
- argument.Value,
- argument.Type));
- }
- var namedArgs =
- (from namedArg in attribute.Fields
- select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value })
- .Concat (
- (from namedArg in attribute.Properties
- select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value }))
- .OrderBy (v => v.Name);
- foreach (var d in namedArgs)
- fields.Add (string.Format ("{0}={1}", d.Name,
- MakeAttributesValueString (d.Value, d.Type)));
-
- string a2 = String.Join(", ", fields.ToArray ());
- if (a2 != "") a2 = "(" + a2 + ")";
-
- string name = attribute.GetDeclaringType();
- if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
- yield return prefix + name + a2;
- }
- }
-
- static readonly string[] ValidExtensionMembers = {
- "Docs",
- "MemberSignature",
- "MemberType",
- "Parameters",
- "ReturnValue",
- "TypeParameters",
- };
-
- static readonly string[] ValidExtensionDocMembers = {
- "param",
- "summary",
- "typeparam",
- };
-
- private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
- {
- MethodDefinition me = info.Member as MethodDefinition;
- if (me == null)
- return;
- if (info.Parameters.Count < 1)
- return;
- if (!DocUtils.IsExtensionMethod (me))
- return;
-
- XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
- XmlNode member = e.CloneNode (true);
- em.AppendChild (member);
- RemoveExcept (member, ValidExtensionMembers);
- RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
- WriteElementText (member, "MemberType", "ExtensionMethod");
- XmlElement link = member.OwnerDocument.CreateElement ("Link");
- var linktype = slashdocFormatter.GetName (me.DeclaringType);
- var linkmember = slashdocFormatter.GetDeclaration (me);
- link.SetAttribute ("Type", linktype);
- link.SetAttribute ("Member", linkmember);
- member.AppendChild (link);
- AddTargets (em, info);
-
- if (!IsMultiAssembly || (IsMultiAssembly && !extensionMethods.Any (ex => ex.SelectSingleNode ("Member/Link/@Type").Value == linktype && ex.SelectSingleNode ("Member/Link/@Member").Value == linkmember))) {
- extensionMethods.Add (em);
- }
- }
-
- private static void RemoveExcept (XmlNode node, string[] except)
- {
- if (node == null)
- return;
- MyXmlNodeList remove = null;
- foreach (XmlNode n in node.ChildNodes) {
- if (Array.BinarySearch (except, n.Name) < 0) {
- if (remove == null)
- remove = new MyXmlNodeList ();
- remove.Add (n);
- }
- }
- if (remove != null)
- foreach (XmlNode n in remove)
- node.RemoveChild (n);
- }
-
- private static void AddTargets (XmlNode member, DocsNodeInfo info)
- {
- XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
- member.PrependChild (targets);
- if (!(info.Parameters [0].ParameterType is GenericParameter)) {
- var reference = info.Parameters[0].ParameterType;
- TypeReference typeReference = reference as TypeReference;
- var declaration = reference != null ?
- slashdocFormatter.GetDeclaration (typeReference) :
- slashdocFormatter.GetDeclaration (reference);
-
- AppendElementAttributeText (targets, "Target", "Type", declaration);
- }
- else {
- GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
- IList<TypeReference> constraints = gp.Constraints;
- if (constraints.Count == 0)
- AppendElementAttributeText (targets, "Target", "Type", "System.Object");
- else
- foreach (TypeReference c in constraints)
- AppendElementAttributeText(targets, "Target", "Type",
- slashdocFormatter.GetDeclaration (c));
- }
- }
-
- private static bool GetFieldConstValue (FieldDefinition field, out string value)
- {
- value = null;
- TypeDefinition type = field.DeclaringType.Resolve ();
- if (type != null && type.IsEnum) return false;
-
- if (type != null && type.IsGenericType ()) return false;
- if (!field.HasConstant)
- return false;
- if (field.IsLiteral) {
- object val = field.Constant;
- if (val == null) value = "null";
- else if (val is Enum) value = val.ToString();
- else if (val is IFormattable) {
- value = ((IFormattable)val).ToString(null, CultureInfo.InvariantCulture);
- if (val is string)
- value = "\"" + value + "\"";
- }
- if (value != null && value != "")
- return true;
- }
- return false;
- }
-
- // XML HELPER FUNCTIONS
-
- internal static XmlElement WriteElement(XmlNode parent, string element, bool forceNewElement = false) {
- XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
- if (ret == null || forceNewElement) {
- string[] path = element.Split('/');
- foreach (string p in path) {
- ret = (XmlElement)parent.SelectSingleNode(p);
- if (ret == null || forceNewElement) {
- string ename = p;
- if (ename.IndexOf('[') >= 0) // strip off XPath predicate
- ename = ename.Substring(0, ename.IndexOf('['));
- ret = parent.OwnerDocument.CreateElement(ename);
- parent.AppendChild(ret);
- parent = ret;
- } else {
- parent = ret;
- }
- }
- }
- return ret;
- }
- private static XmlElement WriteElementText(XmlNode parent, string element, string value, bool forceNewElement = false) {
- XmlElement node = WriteElement(parent, element, forceNewElement: forceNewElement);
- node.InnerText = value;
- return node;
- }
-
- static XmlElement AppendElementText (XmlNode parent, string element, string value)
- {
- XmlElement n = parent.OwnerDocument.CreateElement (element);
- parent.AppendChild (n);
- n.InnerText = value;
- return n;
- }
-
- static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
- {
- XmlElement n = parent.OwnerDocument.CreateElement (element);
- parent.AppendChild (n);
- n.SetAttribute (attribute, value);
- return n;
- }
-
- internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
- {
- XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
- dest.AppendChild (copy);
- return copy;
- }
-
- private static void WriteElementInitialText(XmlElement parent, string element, string value) {
- XmlElement node = (XmlElement)parent.SelectSingleNode(element);
- if (node != null)
- return;
- node = WriteElement(parent, element);
- node.InnerText = value;
- }
- private static XmlElement WriteElementAttribute(XmlElement parent, string element, string attribute, string value, bool forceNewElement = false) {
- XmlElement node = WriteElement(parent, element, forceNewElement:forceNewElement);
- return WriteElementAttribute (parent, node, attribute, value);
- }
- private static XmlElement WriteElementAttribute(XmlElement parent, XmlElement node, string attribute, string value) {
- if (node.GetAttribute (attribute) != value) {
- node.SetAttribute (attribute, value);
- }
- return node;
- }
- internal static void ClearElement(XmlElement parent, string name) {
- XmlElement node = (XmlElement)parent.SelectSingleNode(name);
- if (node != null)
- parent.RemoveChild(node);
- }
-
- // DOCUMENTATION HELPER FUNCTIONS
-
- private void MakeDocNode (DocsNodeInfo info, IEnumerable<DocumentationImporter> setimporters)
- {
- List<GenericParameter> genericParams = info.GenericParameters;
- IList<ParameterDefinition> parameters = info.Parameters;
- TypeReference returntype = info.ReturnType;
- bool returnisreturn = info.ReturnIsReturn;
- XmlElement e = info.Node;
- bool addremarks = info.AddRemarks;
-
- WriteElementInitialText(e, "summary", "To be added.");
-
- if (parameters != null) {
- string[] values = new string [parameters.Count];
- for (int i = 0; i < values.Length; ++i)
- values [i] = parameters [i].Name;
- UpdateParameters (e, "param", values);
- }
-
- if (genericParams != null) {
- string[] values = new string [genericParams.Count];
- for (int i = 0; i < values.Length; ++i)
- values [i] = genericParams [i].Name;
- UpdateParameters (e, "typeparam", values);
- }
-
- string retnodename = null;
- if (returntype != null && returntype.FullName != "System.Void") { // FIXME
- retnodename = returnisreturn ? "returns" : "value";
- string retnodename_other = !returnisreturn ? "returns" : "value";
-
- // If it has a returns node instead of a value node, change its name.
- XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
- if (retother != null) {
- XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
- foreach (XmlNode node in retother)
- retnode.AppendChild(node.CloneNode(true));
- e.ReplaceChild(retnode, retother);
- } else {
- WriteElementInitialText(e, retnodename, "To be added.");
- }
- } else {
- ClearElement(e, "returns");
- ClearElement(e, "value");
- }
-
- if (addremarks)
- WriteElementInitialText(e, "remarks", "To be added.");
-
- if (exceptions.HasValue && info.Member != null &&
- (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
- UpdateExceptions (e, info.Member);
- }
-
- foreach (DocumentationImporter importer in importers) {
- importer.ImportDocumentation (info);
- }
- if (setimporters != null) {
- foreach (var i in setimporters)
- i.ImportDocumentation (info);
- }
-
- OrderDocsNodes (e, e.ChildNodes);
- NormalizeWhitespace(e);
- }
-
- static readonly string[] DocsNodeOrder = {
- "typeparam", "param", "summary", "returns", "value", "remarks",
- };
-
- private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
- {
- ReorderNodes (docs, children, DocsNodeOrder);
- }
-
-
- private void UpdateParameters (XmlElement e, string element, string[] values)
- {
- if (values != null) {
- XmlNode[] paramnodes = new XmlNode[values.Length];
-
- // Some documentation had param nodes with leading spaces.
- foreach (XmlElement paramnode in e.SelectNodes(element)){
- paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
- }
-
- // If a member has only one parameter, we can track changes to
- // the name of the parameter easily.
- if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
- UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
- }
-
- bool reinsert = false;
-
- // Pick out existing and still-valid param nodes, and
- // create nodes for parameters not in the file.
- Hashtable seenParams = new Hashtable();
- for (int pi = 0; pi < values.Length; pi++) {
- string p = values [pi];
- seenParams[p] = pi;
-
- paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
- if (paramnodes[pi] != null) continue;
-
- XmlElement pe = e.OwnerDocument.CreateElement(element);
- pe.SetAttribute("name", p);
- pe.InnerText = "To be added.";
- paramnodes[pi] = pe;
- reinsert = true;
- }
-
- // Remove parameters that no longer exist and check all params are in the right order.
- int idx = 0;
- MyXmlNodeList todelete = new MyXmlNodeList ();
- foreach (XmlElement paramnode in e.SelectNodes(element)) {
- string name = paramnode.GetAttribute("name");
- if (!seenParams.ContainsKey(name)) {
- if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
- Warning ("The following param node can only be deleted if the --delete option is given: ");
- if (e.ParentNode == e.OwnerDocument.DocumentElement) {
- // delegate type
- Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
- e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
- name);
- }
- else {
- Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
- e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
- e.ParentNode.Attributes ["MemberName"].Value,
- name);
- }
- Warning ("\tValue={0}", paramnode.OuterXml);
- } else {
- todelete.Add (paramnode);
- }
- continue;
- }
-
- if ((int)seenParams[name] != idx)
- reinsert = true;
-
- idx++;
- }
-
- foreach (XmlNode n in todelete) {
- n.ParentNode.RemoveChild (n);
- }
-
- // Re-insert the parameter nodes at the top of the doc section.
- if (reinsert)
- for (int pi = values.Length-1; pi >= 0; pi--)
- e.PrependChild(paramnodes[pi]);
- } else {
- // Clear all existing param nodes
- foreach (XmlNode paramnode in e.SelectNodes(element)) {
- if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
- Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
- Console.WriteLine(paramnode.OuterXml);
- } else {
- paramnode.ParentNode.RemoveChild(paramnode);
- }
- }
- }
- }
-
- private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
- {
- string existingName = pe.GetAttribute ("name");
- pe.SetAttribute ("name", newName);
- if (existingName == newName)
- return;
- foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
- if (paramref.GetAttribute ("name").Trim () == existingName)
- paramref.SetAttribute ("name", newName);
- }
-
- class CrefComparer : XmlNodeComparer {
-
- public CrefComparer ()
- {
- }
-
- public override int Compare (XmlNode x, XmlNode y)
- {
- string xType = x.Attributes ["cref"].Value;
- string yType = y.Attributes ["cref"].Value;
- string xNamespace = GetNamespace (xType);
- string yNamespace = GetNamespace (yType);
-
- int c = xNamespace.CompareTo (yNamespace);
- if (c != 0)
- return c;
- return xType.CompareTo (yType);
- }
-
- static string GetNamespace (string type)
- {
- int n = type.LastIndexOf ('.');
- if (n >= 0)
- return type.Substring (0, n);
- return string.Empty;
- }
- }
-
- private void UpdateExceptions (XmlNode docs, MemberReference member)
- {
- string indent = new string (' ', 10);
- foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
- string cref = slashdocFormatter.GetDeclaration (source.Exception);
- var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
- if (node != null)
- continue;
- XmlElement e = docs.OwnerDocument.CreateElement ("exception");
- e.SetAttribute ("cref", cref);
- e.InnerXml = "To be added; from:\n" + indent + "<see cref=\"" +
- string.Join ("\" />,\n" + indent + "<see cref=\"",
- source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
- .OrderBy (s => s)) +
- "\" />";
- docs.AppendChild (e);
- }
- SortXmlNodes (docs, docs.SelectNodes ("exception"),
- new CrefComparer ());
- }
-
- private static void NormalizeWhitespace(XmlElement e) {
- // Remove all text and whitespace nodes from the element so it
- // is outputted with nice indentation and no blank lines.
- ArrayList deleteNodes = new ArrayList();
- foreach (XmlNode n in e)
- if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
- deleteNodes.Add(n);
- foreach (XmlNode n in deleteNodes)
- n.ParentNode.RemoveChild(n);
- }
-
- private bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
- {
- TypeDefinition type = member as TypeDefinition;
- if (type == null)
- type = member.DeclaringType as TypeDefinition;
-
- var versions = new string[] { GetAssemblyVersion (type.Module.Assembly) };
-
- if (root.LocalName == "AssemblyInfo")
- return UpdateAssemblyVersionForAssemblyInfo (root, root.ParentNode as XmlElement, versions, add: true);
- else
- return UpdateAssemblyVersions (root, type.Module.Assembly, versions, add);
- }
-
- private static string GetAssemblyVersion (AssemblyDefinition assembly)
- {
- return assembly.Name.Version.ToString();
- }
-
- private bool UpdateAssemblyVersions(XmlElement root, AssemblyDefinition assembly, string[] assemblyVersions, bool add)
- {
- if (IsMultiAssembly)
- return false;
-
- XmlElement av = (XmlElement) root.SelectSingleNode ("AssemblyVersions");
- if (av != null) {
- // AssemblyVersions is not part of the spec
- root.RemoveChild (av);
- }
-
- string oldNodeFilter = "AssemblyInfo[not(@apistyle) or @apistyle='classic']";
- string newNodeFilter = "AssemblyInfo[@apistyle='unified']";
- string thisNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? newNodeFilter : oldNodeFilter;
- string thatNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? oldNodeFilter : newNodeFilter;
-
- XmlElement e = (XmlElement) root.SelectSingleNode (thisNodeFilter);
- if (e == null) {
- e = root.OwnerDocument.CreateElement("AssemblyInfo");
-
- if (MDocUpdater.HasDroppedNamespace (assembly)) {
- e.AddApiStyle (ApiStyle.Unified);
- }
-
- root.AppendChild(e);
- }
-
- var thatNode = (XmlElement) root.SelectSingleNode (thatNodeFilter);
- if (MDocUpdater.HasDroppedNamespace (assembly) && thatNode != null) {
- // there's a classic node, we should add apistyles
- e.AddApiStyle (ApiStyle.Unified);
- thatNode.AddApiStyle (ApiStyle.Classic);
- }
-
- return UpdateAssemblyVersionForAssemblyInfo (e, root, assemblyVersions, add);
- }
-
- static bool UpdateAssemblyVersionForAssemblyInfo (XmlElement e, XmlElement root, string[] assemblyVersions, bool add)
- {
- List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().Where (v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0).ToList ();
- // matches.Count > 0 && add: ignore -- already present
- if (matches.Count > 0 && !add) {
- foreach (XmlNode c in matches)
- e.RemoveChild (c);
- }
- else if (matches.Count == 0 && add) {
- foreach (string sv in assemblyVersions) {
- XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
- c.InnerText = sv;
- e.AppendChild(c);
- }
- }
-
- // matches.Count == 0 && !add: ignore -- already not present
- XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
- SortXmlNodes (e, avs, new VersionComparer ());
-
- bool anyNodesLeft = avs.Count != 0;
- if (!anyNodesLeft) {
- e.ParentNode.RemoveChild (e);
- }
- return anyNodesLeft;
- }
-
- // FIXME: get TypeReferences instead of string comparison?
- private static string[] IgnorableAttributes = {
- // Security related attributes
- "System.Reflection.AssemblyKeyFileAttribute",
- "System.Reflection.AssemblyDelaySignAttribute",
- // Present in @RefType
- "System.Runtime.InteropServices.OutAttribute",
- // For naming the indexer to use when not using indexers
- "System.Reflection.DefaultMemberAttribute",
- // for decimal constants
- "System.Runtime.CompilerServices.DecimalConstantAttribute",
- // compiler generated code
- "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
- // more compiler generated code, e.g. iterator methods
- "System.Diagnostics.DebuggerHiddenAttribute",
- "System.Runtime.CompilerServices.FixedBufferAttribute",
- "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
- // extension methods
- "System.Runtime.CompilerServices.ExtensionAttribute",
- // Used to differentiate 'object' from C#4 'dynamic'
- "System.Runtime.CompilerServices.DynamicAttribute",
- };
-
- private IEnumerable<char> FilterSpecialChars (string value)
- {
- foreach (char c in value) {
- switch (c) {
- case '\0':
- yield return '\\';
- yield return '0';
- break;
- case '\t':
- yield return '\\';
- yield return 't';
- break;
- case '\n':
- yield return '\\';
- yield return 'n';
- break;
- case '\r':
- yield return '\\';
- yield return 'r';
- break;
- case '\f':
- yield return '\\';
- yield return 'f';
- break;
- case '\b':
- yield return '\\';
- yield return 'b';
- break;
- default:
- yield return c;
- break;
- }
- }
- }
-
- private void MakeAttributes (XmlElement root, IEnumerable<string> attributes, TypeReference t=null)
- {
- if (!attributes.Any ()) {
- ClearElement (root, "Attributes");
- return;
- }
-
- XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
- if (e != null)
- e.RemoveAll();
- else if (e == null)
- e = root.OwnerDocument.CreateElement("Attributes");
-
-
- foreach (string attribute in attributes) {
- XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
- e.AppendChild(ae);
- var value = new String (FilterSpecialChars (attribute).ToArray ());
- WriteElementText(ae, "AttributeName", value);
- }
-
- if (e.ParentNode == null)
- root.AppendChild(e);
-
- NormalizeWhitespace(e);
- }
-
- public static string MakeAttributesValueString (object v, TypeReference valueType)
- {
- var formatters = new [] {
- new AttributeValueFormatter (),
- new ApplePlatformEnumFormatter (),
- new StandardFlagsEnumFormatter (),
- new DefaultAttributeValueFormatter (),
- };
-
- ResolvedTypeInfo type = new ResolvedTypeInfo (valueType);
- foreach (var formatter in formatters) {
- string formattedValue;
- if (formatter.TryFormatValue (v, type, out formattedValue)) {
- return formattedValue;
- }
- }
-
- // this should never occur because the DefaultAttributeValueFormatter will always
- // successfully format the value ... but this is needed to satisfy the compiler :)
- throw new InvalidDataException (string.Format ("Unable to format attribute value ({0})", v.ToString ()));
- }
-
- internal static IDictionary<long, string> GetEnumerationValues (TypeDefinition type)
- {
- var values = new Dictionary<long, string> ();
- foreach (var f in
- (from f in type.Fields
- where !(f.IsRuntimeSpecialName || f.IsSpecialName)
- select f)) {
- values [ToInt64 (f.Constant)] = f.Name;
- }
- return values;
- }
-
- internal static long ToInt64 (object value)
- {
- if (value is ulong)
- return (long) (ulong) value;
- return Convert.ToInt64 (value);
- }
-
- private void MakeParameters (XmlElement root, MemberReference member, IList<ParameterDefinition> parameters, bool shouldDuplicateWithNew=false)
- {
- XmlElement e = WriteElement(root, "Parameters");
-
- int i = 0;
- foreach (ParameterDefinition p in parameters) {
- XmlElement pe;
-
- // param info
- var ptype = GetDocParameterType (p.ParameterType);
- var newPType = ptype;
-
- if (MDocUpdater.SwitchingToMagicTypes) {
- newPType = NativeTypeManager.ConvertFromNativeType (ptype);
- }
-
- // now find the existing node, if it's there so we can reuse it.
- var nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
- .Cast<XmlElement> ().Where (x => x.GetAttribute ("Name") == p.Name)
- .ToArray();
-
- if (nodes.Count () == 0) {
- // wasn't found, let's make sure it wasn't just cause the param name was changed
- nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
- .Cast<XmlElement> ()
- .Skip (i) // this makes sure we don't inadvertently "reuse" nodes when adding new ones
- .Where (x => x.GetAttribute ("Name") != p.Name && (x.GetAttribute ("Type") == ptype || x.GetAttribute ("Type") == newPType))
- .Take(1) // there might be more than one that meets this parameter ... only take the first.
- .ToArray();
- }
-
- AddXmlNode (nodes,
- x => x.GetAttribute ("Type") == ptype,
- x => x.SetAttribute ("Type", ptype),
- () => {
- pe = root.OwnerDocument.CreateElement ("Parameter");
- e.AppendChild (pe);
-
- pe.SetAttribute ("Name", p.Name);
- pe.SetAttribute ("Type", ptype);
- if (p.ParameterType is ByReferenceType) {
- if (p.IsOut)
- pe.SetAttribute ("RefType", "out");
- else
- pe.SetAttribute ("RefType", "ref");
- }
-
- MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
- return pe;
- },
- member);
-
- i++;
- }
- }
-
- private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams, MemberReference member, bool shouldDuplicateWithNew)
- {
- if (typeParams == null || typeParams.Count == 0) {
- XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
- if (f != null)
- root.RemoveChild (f);
- return;
- }
- XmlElement e = WriteElement(root, "TypeParameters");
-
- var nodes = e.SelectNodes ("TypeParameter").Cast<XmlElement> ().ToArray ();
-
- foreach (GenericParameter t in typeParams) {
-
- IList<TypeReference> constraints = t.Constraints;
- GenericParameterAttributes attrs = t.Attributes;
-
-
- AddXmlNode (
- nodes,
- x => {
- var baseType = e.SelectSingleNode("BaseTypeName");
- // TODO: should this comparison take into account BaseTypeName?
- return x.GetAttribute("Name") == t.Name;
- },
- x => {}, // no additional action required
- () => {
-
- XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
- e.AppendChild(pe);
- pe.SetAttribute("Name", t.Name);
- MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""), t.DeclaringType);
- XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
- if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
- if (ce != null)
- e.RemoveChild (ce);
- return pe;
- }
- if (ce != null)
- ce.RemoveAll();
- else {
- ce = root.OwnerDocument.CreateElement ("Constraints");
- }
- pe.AppendChild (ce);
- if ((attrs & GenericParameterAttributes.Contravariant) != 0)
- AppendElementText (ce, "ParameterAttribute", "Contravariant");
- if ((attrs & GenericParameterAttributes.Covariant) != 0)
- AppendElementText (ce, "ParameterAttribute", "Covariant");
- if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
- AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
- if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
- AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
- if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
- AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
- foreach (TypeReference c in constraints) {
- TypeDefinition cd = c.Resolve ();
- AppendElementText (ce,
- (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
- GetDocTypeFullName (c));
- }
-
- return pe;
- },
- member);
- }
- }
-
- private void MakeParameters (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew)
- {
- if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
- MakeParameters (root, mi, ((MethodDefinition)mi).Parameters, shouldDuplicateWithNew);
- else if (mi is MethodDefinition) {
- MethodDefinition mb = (MethodDefinition) mi;
- IList<ParameterDefinition> parameters = mb.Parameters;
- MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
- if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
- XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
- p.SetAttribute ("RefType", "this");
- }
- }
- else if (mi is PropertyDefinition) {
- IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
- if (parameters.Count > 0)
- MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
- else
- return;
- }
- else if (mi is FieldDefinition) return;
- else if (mi is EventDefinition) return;
- else throw new ArgumentException();
- }
-
- internal static string GetDocParameterType (TypeReference type)
- {
- return GetDocTypeFullName (type).Replace ("@", "&");
- }
-
- private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes, bool shouldDuplicateWithNew=false)
- {
- XmlElement e = WriteElement(root, "ReturnValue");
- var valueToUse = GetDocTypeFullName (type);
-
- AddXmlNode (e.SelectNodes("ReturnType").Cast<XmlElement> ().ToArray (),
- x => x.InnerText == valueToUse,
- x => x.InnerText = valueToUse,
- () => {
- var newNode = WriteElementText(e, "ReturnType", valueToUse, forceNewElement: true);
- if (attributes != null)
- MakeAttributes(e, GetCustomAttributes (attributes, ""), type);
-
- return newNode;
- },
- type);
- }
-
- private void MakeReturnValue (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew=false)
- {
- if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
- return;
- else if (mi is MethodDefinition)
- MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes, shouldDuplicateWithNew);
- else if (mi is PropertyDefinition)
- MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null, shouldDuplicateWithNew);
- else if (mi is FieldDefinition)
- MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null, shouldDuplicateWithNew);
- else if (mi is EventDefinition)
- MakeReturnValue (root, ((EventDefinition)mi).EventType, null, shouldDuplicateWithNew);
- else
- throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
- }
-
- private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info, XmlNode members, FrameworkTypeEntry typeEntry)
- {
- MemberReference mi = info.Member;
- if (mi is TypeDefinition) return null;
-
- string sigs = memberFormatters [0].GetDeclaration (mi);
- if (sigs == null) return null; // not publicly visible
-
- // no documentation for property/event accessors. Is there a better way of doing this?
- if (mi.Name.StartsWith("get_", StringComparison.Ordinal)) return null;
- if (mi.Name.StartsWith("set_", StringComparison.Ordinal)) return null;
- if (mi.Name.StartsWith("add_", StringComparison.Ordinal)) return null;
- if (mi.Name.StartsWith("remove_", StringComparison.Ordinal)) return null;
- if (mi.Name.StartsWith("raise_", StringComparison.Ordinal)) return null;
-
- XmlElement me = doc.CreateElement("Member");
- members.AppendChild (me);
- me.SetAttribute("MemberName", GetMemberName (mi));
-
- info.Node = me;
- UpdateMember(info, typeEntry);
- if (exceptions.HasValue &&
- (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
- UpdateExceptions (info.Node, info.Member);
-
- if (since != null) {
- XmlNode docs = me.SelectSingleNode("Docs");
- docs.AppendChild (CreateSinceNode (doc));
- }
-
- return me;
- }
-
- internal static string GetMemberName (MemberReference mi)
- {
- MethodDefinition mb = mi as MethodDefinition;
- if (mb == null) {
- PropertyDefinition pi = mi as PropertyDefinition;
- if (pi == null)
- return mi.Name;
- return DocUtils.GetPropertyName (pi);
- }
- StringBuilder sb = new StringBuilder (mi.Name.Length);
- if (!DocUtils.IsExplicitlyImplemented (mb))
- sb.Append (mi.Name);
- else {
- TypeReference iface;
- MethodReference ifaceMethod;
- DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
- sb.Append (GetDocTypeFullName (iface));
- sb.Append ('.');
- sb.Append (ifaceMethod.Name);
- }
- if (mb.IsGenericMethod ()) {
- IList<GenericParameter> typeParams = mb.GenericParameters;
- if (typeParams.Count > 0) {
- sb.Append ("<");
- sb.Append (typeParams [0].Name);
- for (int i = 1; i < typeParams.Count; ++i)
- sb.Append (",").Append (typeParams [i].Name);
- sb.Append (">");
- }
- }
- return sb.ToString ();
- }
-
- /// SIGNATURE GENERATION FUNCTIONS
- internal static bool IsPrivate (MemberReference mi)
- {
- return memberFormatters [0].GetDeclaration (mi) == null;
- }
-
- internal static string GetMemberType (MemberReference mi)
- {
- if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
- return "Constructor";
- if (mi is MethodDefinition)
- return "Method";
- if (mi is PropertyDefinition)
- return "Property";
- if (mi is FieldDefinition)
- return "Field";
- if (mi is EventDefinition)
- return "Event";
- throw new ArgumentException();
- }
-
- private static string GetDocTypeName (TypeReference type)
- {
- return docTypeFormatter.GetName (type);
- }
-
- internal static string GetDocTypeFullName (TypeReference type)
- {
- return DocTypeFullMemberFormatter.Default.GetName (type);
- }
-
- internal static string GetXPathForMember (DocumentationMember member)
- {
- StringBuilder xpath = new StringBuilder ();
- xpath.Append ("//Members/Member[@MemberName=\"")
- .Append (member.MemberName)
- .Append ("\"]");
- if (member.Parameters != null && member.Parameters.Count > 0) {
- xpath.Append ("/Parameters[count(Parameter) = ")
- .Append (member.Parameters.Count);
- for (int i = 0; i < member.Parameters.Count; ++i) {
- xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
- xpath.Append (member.Parameters [i]);
- xpath.Append ("\"");
- }
- xpath.Append ("]/..");
- }
- return xpath.ToString ();
- }
-
- public static string GetXPathForMember (XPathNavigator member)
- {
- StringBuilder xpath = new StringBuilder ();
- xpath.Append ("//Type[@FullName=\"")
- .Append (member.SelectSingleNode ("../../@FullName").Value)
- .Append ("\"]/");
- xpath.Append ("Members/Member[@MemberName=\"")
- .Append (member.SelectSingleNode ("@MemberName").Value)
- .Append ("\"]");
- XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
- if (parameters.Count > 0) {
- xpath.Append ("/Parameters[count(Parameter) = ")
- .Append (parameters.Count);
- int i = 0;
- while (parameters.MoveNext ()) {
- ++i;
- xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
- xpath.Append (parameters.Current.Value);
- xpath.Append ("\"");
- }
- xpath.Append ("]/..");
- }
- return xpath.ToString ();
- }
-
- public static string GetXPathForMember (MemberReference member)
- {
- StringBuilder xpath = new StringBuilder ();
- xpath.Append ("//Type[@FullName=\"")
- .Append (member.DeclaringType.FullName)
- .Append ("\"]/");
- xpath.Append ("Members/Member[@MemberName=\"")
- .Append (GetMemberName (member))
- .Append ("\"]");
-
- IList<ParameterDefinition> parameters = null;
- if (member is MethodDefinition)
- parameters = ((MethodDefinition) member).Parameters;
- else if (member is PropertyDefinition) {
- parameters = ((PropertyDefinition) member).Parameters;
- }
- if (parameters != null && parameters.Count > 0) {
- xpath.Append ("/Parameters[count(Parameter) = ")
- .Append (parameters.Count);
- for (int i = 0; i < parameters.Count; ++i) {
- xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
- xpath.Append (GetDocParameterType (parameters [i].ParameterType));
- xpath.Append ("\"");
- }
- xpath.Append ("]/..");
- }
- return xpath.ToString ();
- }
-}
-
-static class CecilExtensions {
- public static string GetDeclaringType(this CustomAttribute attribute)
- {
- var type = attribute.Constructor.DeclaringType;
- var typeName = type.FullName;
-
- string translatedType = NativeTypeManager.GetTranslatedName (type);
- return translatedType;
- }
-
- public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
- {
- foreach (var c in type.Methods.Where (m => m.IsConstructor))
- yield return (MemberReference) c;
- foreach (var e in type.Events)
- yield return (MemberReference) e;
- foreach (var f in type.Fields)
- yield return (MemberReference) f;
- foreach (var m in type.Methods.Where (m => !m.IsConstructor))
- yield return (MemberReference) m;
- foreach (var t in type.NestedTypes)
- yield return (MemberReference) t;
- foreach (var p in type.Properties)
- yield return (MemberReference) p;
- }
-
- public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
- {
- return GetMembers (type).Where (m => m.Name == member);
- }
-
- public static MemberReference GetMember (this TypeDefinition type, string member)
- {
- return GetMembers (type, member).EnsureZeroOrOne ();
- }
-
- static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
- {
- if (source.Count () > 1)
- throw new InvalidOperationException ("too many matches");
- return source.FirstOrDefault ();
- }
-
- public static MethodDefinition GetMethod (this TypeDefinition type, string method)
- {
- return type.Methods
- .Where (m => m.Name == method)
- .EnsureZeroOrOne ();
- }
-
- public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
- {
- TypeDefinition def = type as TypeDefinition;
- if (def == null)
- return new MemberReference [0];
- CustomAttribute defMemberAttr = def.CustomAttributes
- .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
- if (defMemberAttr == null)
- return new MemberReference [0];
- string name = (string) defMemberAttr.ConstructorArguments [0].Value;
- return def.Properties
- .Where (p => p.Name == name)
- .Select (p => (MemberReference) p);
- }
-
- public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
- {
- return assembly.Modules.SelectMany (md => md.GetAllTypes ());
- }
-
- public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
- {
- return GetTypes (assembly)
- .Where (td => td.FullName == type)
- .EnsureZeroOrOne ();
- }
-
- public static bool IsGenericType (this TypeReference type)
- {
- return type.GenericParameters.Count > 0;
- }
-
- public static bool IsGenericMethod (this MethodReference method)
- {
- return method.GenericParameters.Count > 0;
- }
-
- public static TypeReference GetUnderlyingType (this TypeDefinition type)
- {
- if (!type.IsEnum)
- return type;
- return type.Fields.First (f => f.Name == "value__").FieldType;
- }
-
- public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
- {
- return self.Types.SelectMany (t => t.GetAllTypes ());
- }
-
- static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
- {
- yield return self;
-
- if (!self.HasNestedTypes)
- yield break;
-
- foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
- yield return type;
- }
-}
-
-enum ApiStyle {
- Classic,
- Unified
-}
-
-static class DocUtils {
-
- public static bool DoesNotHaveApiStyle(this XmlElement element, ApiStyle style) {
- string styleString = style.ToString ().ToLowerInvariant ();
- string apistylevalue = element.GetAttribute ("apistyle");
- return apistylevalue != styleString || string.IsNullOrWhiteSpace(apistylevalue);
- }
- public static bool HasApiStyle(this XmlElement element, ApiStyle style) {
- string styleString = style.ToString ().ToLowerInvariant ();
- return element.GetAttribute ("apistyle") == styleString;
- }
- public static bool HasApiStyle(this XmlNode node, ApiStyle style)
- {
- var attribute = node.Attributes ["apistyle"];
- return attribute != null && attribute.Value == style.ToString ().ToLowerInvariant ();
- }
- public static void AddApiStyle(this XmlElement element, ApiStyle style) {
- string styleString = style.ToString ().ToLowerInvariant ();
- var existingValue = element.GetAttribute ("apistyle");
- if (string.IsNullOrWhiteSpace (existingValue) || existingValue != styleString) {
- element.SetAttribute ("apistyle", styleString);
- }
-
- // Propagate the API style up to the membernode if necessary
- if (element.LocalName == "AssemblyInfo" && element.ParentNode != null && element.ParentNode.LocalName == "Member") {
- var member = element.ParentNode;
- var unifiedAssemblyNode = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']");
- var classicAssemblyNode = member.SelectSingleNode ("AssemblyInfo[not(@apistyle) or @apistyle='classic']");
-
- var parentAttribute = element.ParentNode.Attributes ["apistyle"];
- Action removeStyle = () => element.ParentNode.Attributes.Remove (parentAttribute);
- Action propagateStyle = () => {
- if (parentAttribute == null) {
- // if it doesn't have the attribute, then add it
- parentAttribute = element.OwnerDocument.CreateAttribute ("apistyle");
- parentAttribute.Value = styleString;
- element.ParentNode.Attributes.Append (parentAttribute);
- }
- };
-
- if ((style == ApiStyle.Classic && unifiedAssemblyNode != null) || (style == ApiStyle.Unified && classicAssemblyNode != null))
- removeStyle ();
- else
- propagateStyle ();
- }
- }
- public static void AddApiStyle (this XmlNode node, ApiStyle style)
- {
- string styleString = style.ToString ().ToLowerInvariant ();
- var existingAttribute = node.Attributes ["apistyle"];
- if (existingAttribute == null) {
- existingAttribute = node.OwnerDocument.CreateAttribute ("apistyle");
- node.Attributes.Append (existingAttribute);
- }
- existingAttribute.Value = styleString;
- }
- public static void RemoveApiStyle (this XmlElement element, ApiStyle style)
- {
- string styleString = style.ToString ().ToLowerInvariant ();
- string existingValue = element.GetAttribute ("apistyle");
- if (string.IsNullOrWhiteSpace (existingValue) || existingValue == styleString) {
- element.RemoveAttribute ("apistyle");
- }
- }
- public static void RemoveApiStyle (this XmlNode node, ApiStyle style)
- {
- var styleAttribute = node.Attributes ["apistyle"];
- if (styleAttribute != null && styleAttribute.Value == style.ToString ().ToLowerInvariant ()) {
- node.Attributes.Remove (styleAttribute);
- }
- }
-
- public static bool IsExplicitlyImplemented (MethodDefinition method)
- {
- return method != null && method.IsPrivate && method.IsFinal && method.IsVirtual;
- }
-
- public static string GetTypeDotMember (string name)
- {
- int startType, startMethod;
- startType = startMethod = -1;
- for (int i = 0; i < name.Length; ++i) {
- if (name [i] == '.') {
- startType = startMethod;
- startMethod = i;
- }
- }
- return name.Substring (startType+1);
- }
-
- public static string GetMember (string name)
- {
- int i = name.LastIndexOf ('.');
- if (i == -1)
- return name;
- return name.Substring (i+1);
- }
-
- public static void GetInfoForExplicitlyImplementedMethod (
- MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
- {
- iface = null;
- ifaceMethod = null;
- if (method.Overrides.Count != 1)
- throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
- iface = method.Overrides [0].DeclaringType;
- ifaceMethod = method.Overrides [0];
- }
-
- public static string GetPropertyName (PropertyDefinition pi)
- {
- // Issue: (g)mcs-generated assemblies that explicitly implement
- // properties don't specify the full namespace, just the
- // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
- MethodDefinition method = pi.GetMethod;
- if (method == null)
- method = pi.SetMethod;
- if (!IsExplicitlyImplemented (method))
- return pi.Name;
-
- // Need to determine appropriate namespace for this member.
- TypeReference iface;
- MethodReference ifaceMethod;
- GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
- return string.Join (".", new string[]{
- DocTypeFullMemberFormatter.Default.GetName (iface),
- GetMember (pi.Name)});
- }
-
- public static string GetNamespace (TypeReference type)
- {
- if (type.GetElementType ().IsNested)
- type = type.GetElementType ();
- while (type != null && type.IsNested)
- type = type.DeclaringType;
- if (type == null)
- return string.Empty;
-
- string typeNS = type.Namespace;
-
- // first, make sure this isn't a type reference to another assembly/module
-
- bool isInAssembly = MDocUpdater.IsInAssemblies(type.Module.Name);
- if (isInAssembly && !typeNS.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (type)) {
- typeNS = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typeNS);
- }
- return typeNS;
- }
-
- public static string PathCombine (string dir, string path)
- {
- if (dir == null)
- dir = "";
- if (path == null)
- path = "";
- return Path.Combine (dir, path);
- }
-
- public static bool IsExtensionMethod (MethodDefinition method)
- {
- return
- method.CustomAttributes
- .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
- && method.DeclaringType.CustomAttributes
- .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
- }
-
- public static bool IsDelegate (TypeDefinition type)
- {
- TypeReference baseRef = type.BaseType;
- if (baseRef == null)
- return false;
- return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
- baseRef.FullName == "System.MulticastDelegate";
- }
-
- public static List<TypeReference> GetDeclaringTypes (TypeReference type)
- {
- List<TypeReference> decls = new List<TypeReference> ();
- decls.Add (type);
- while (type.DeclaringType != null) {
- decls.Add (type.DeclaringType);
- type = type.DeclaringType;
- }
- decls.Reverse ();
- return decls;
- }
-
- public static int GetGenericArgumentCount (TypeReference type)
- {
- GenericInstanceType inst = type as GenericInstanceType;
- return inst != null
- ? inst.GenericArguments.Count
- : type.GenericParameters.Count;
- }
-
- public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
- {
- HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
- List<TypeReference> userInterfaces = new List<TypeReference> ();
- foreach (var ii in type.Interfaces) {
- var iface = ii.InterfaceType;
- TypeReference lookup = iface.Resolve () ?? iface;
- if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
- userInterfaces.Add (iface);
- }
- return userInterfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()));
- }
-
- private static string GetQualifiedTypeName (TypeReference type)
- {
- return "[" + type.Scope.Name + "]" + type.FullName;
- }
-
- private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
- {
- HashSet<string> inheritedInterfaces = new HashSet<string> ();
- Action<TypeDefinition> a = null;
- a = t => {
- if (t == null) return;
- foreach (var r in t.Interfaces) {
- inheritedInterfaces.Add (GetQualifiedTypeName (r.InterfaceType));
- a (r.InterfaceType.Resolve ());
- }
- };
- TypeReference baseRef = type.BaseType;
- while (baseRef != null) {
- TypeDefinition baseDef = baseRef.Resolve ();
- if (baseDef != null) {
- a (baseDef);
- baseRef = baseDef.BaseType;
- }
- else
- baseRef = null;
- }
- foreach (var r in type.Interfaces)
- a (r.InterfaceType.Resolve ());
- return inheritedInterfaces;
- }
-}
-
-class DocsNodeInfo {
- public DocsNodeInfo (XmlElement node)
- {
- this.Node = node;
- }
-
- public DocsNodeInfo (XmlElement node, TypeDefinition type)
- : this (node)
- {
- SetType (type);
- }
-
- public DocsNodeInfo (XmlElement node, MemberReference member)
- : this (node)
- {
- SetMemberInfo (member);
- }
-
- void SetType (TypeDefinition type)
- {
- if (type == null)
- throw new ArgumentNullException ("type");
- Type = type;
- GenericParameters = new List<GenericParameter> (type.GenericParameters);
- List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
- int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
- for (int i = 0; i < declTypes.Count - 1; ++i) {
- int remove = System.Math.Min (maxGenArgs,
- DocUtils.GetGenericArgumentCount (declTypes [i]));
- maxGenArgs -= remove;
- while (remove-- > 0)
- GenericParameters.RemoveAt (0);
- }
- if (DocUtils.IsDelegate (type)) {
- Parameters = type.GetMethod("Invoke").Parameters;
- ReturnType = type.GetMethod("Invoke").ReturnType;
- ReturnIsReturn = true;
- }
- }
-
- void SetMemberInfo (MemberReference member)
- {
- if (member == null)
- throw new ArgumentNullException ("member");
- ReturnIsReturn = true;
- AddRemarks = true;
- Member = member;
-
- if (member is MethodReference ) {
- MethodReference mr = (MethodReference) member;
- Parameters = mr.Parameters;
- if (mr.IsGenericMethod ()) {
- GenericParameters = new List<GenericParameter> (mr.GenericParameters);
- }
- }
- else if (member is PropertyDefinition) {
- Parameters = ((PropertyDefinition) member).Parameters;
- }
-
- if (member is MethodDefinition) {
- ReturnType = ((MethodDefinition) member).ReturnType;
- } else if (member is PropertyDefinition) {
- ReturnType = ((PropertyDefinition) member).PropertyType;
- ReturnIsReturn = false;
- }
-
- // no remarks section for enum members
- if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
- AddRemarks = false;
- }
-
- public TypeReference ReturnType;
- public List<GenericParameter> GenericParameters;
- public IList<ParameterDefinition> Parameters;
- public bool ReturnIsReturn;
- public XmlElement Node;
- public bool AddRemarks = true;
- public MemberReference Member;
- public TypeDefinition Type;
-
- public override string ToString ()
- {
- return string.Format ("{0} - {1} - {2}", Type, Member, Node == null ? "no xml" : "with xml");
- }
-}
-
-class DocumentationEnumerator {
-
- public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
- {
- return GetDocumentationTypes (assembly, forTypes, null);
- }
-
- protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
- {
- foreach (TypeDefinition type in assembly.GetTypes()) {
- if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
- continue;
- if (seen != null && seen.Contains (type.FullName))
- continue;
- yield return type;
- foreach (TypeDefinition nested in type.NestedTypes)
- yield return nested;
- }
- }
-
- public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
- {
- foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
- if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
- oldmember.RemoveAttribute ("__monodocer-seen__");
- continue;
- }
- MemberReference m = GetMember (type, new DocumentationMember (oldmember));
- if (m == null) {
- yield return new DocsNodeInfo (oldmember);
- }
- else {
- yield return new DocsNodeInfo (oldmember, m);
- }
- }
- }
-
- protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
- {
- string membertype = member.MemberType;
-
- string returntype = member.ReturnType;
-
- string docName = member.MemberName;
-
- string[] docTypeParams = GetTypeParameters (docName, member.TypeParameters);
-
- // If we're using 'magic types', then we might get false positives ... in those cases, we keep searching
- MemberReference likelyCandidate = null;
-
- // Loop through all members in this type with the same name
- var reflectedMembers = GetReflectionMembers (type, docName).ToArray ();
- foreach (MemberReference mi in reflectedMembers) {
- bool matchedMagicType = false;
- if (mi is TypeDefinition) continue;
- if (MDocUpdater.GetMemberType(mi) != membertype) continue;
-
- if (MDocUpdater.IsPrivate (mi))
- continue;
-
- IList<ParameterDefinition> pis = null;
- string[] typeParams = null;
- if (mi is MethodDefinition) {
- MethodDefinition mb = (MethodDefinition) mi;
- pis = mb.Parameters;
- if (mb.IsGenericMethod ()) {
- IList<GenericParameter> args = mb.GenericParameters;
- typeParams = args.Select (p => p.Name).ToArray ();
- }
- }
- else if (mi is PropertyDefinition)
- pis = ((PropertyDefinition)mi).Parameters;
-
- // check type parameters
- int methodTcount = member.TypeParameters == null ? 0 : member.TypeParameters.Count;
- int reflectionTcount = typeParams == null ? 0 : typeParams.Length;
- if (methodTcount != reflectionTcount)
- continue;
-
- // check member parameters
- int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
- int pcount = pis == null ? 0 : pis.Count;
- if (mcount != pcount)
- continue;
-
- MethodDefinition mDef = mi as MethodDefinition;
- if (mDef != null && !mDef.IsConstructor) {
- // Casting operators can overload based on return type.
- string rtype = GetReplacedString (
- MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
- typeParams, docTypeParams);
- string originalRType = rtype;
- if (MDocUpdater.SwitchingToMagicTypes) {
- rtype = NativeTypeManager.ConvertFromNativeType (rtype);
-
- }
- if ((returntype != rtype && originalRType == rtype) ||
- (MDocUpdater.SwitchingToMagicTypes && returntype != originalRType && returntype != rtype && originalRType != rtype)) {
- continue;
- }
-
- if (originalRType != rtype)
- matchedMagicType = true;
- }
-
- if (pcount == 0)
- return mi;
- bool good = true;
- for (int i = 0; i < pis.Count; i++) {
- string paramType = GetReplacedString (
- MDocUpdater.GetDocParameterType (pis [i].ParameterType),
- typeParams, docTypeParams);
-
- // if magictypes, replace paramType to "classic value" ... so the comparison works
- string originalParamType = paramType;
- if (MDocUpdater.SwitchingToMagicTypes) {
- paramType = NativeTypeManager.ConvertFromNativeType (paramType);
- }
-
- string xmlMemberType = member.Parameters [i];
- if ((!paramType.Equals(xmlMemberType) && paramType.Equals(originalParamType)) ||
- (MDocUpdater.SwitchingToMagicTypes && !originalParamType.Equals(xmlMemberType) && !paramType.Equals(xmlMemberType) && !paramType.Equals(originalParamType))) {
-
- // did not match ... if we're dropping the namespace, and the paramType has the dropped
- // namespace, we should see if it matches when added
- bool stillDoesntMatch = true;
- if (MDocUpdater.HasDroppedNamespace(type) && paramType.StartsWith (MDocUpdater.droppedNamespace)) {
- string withDroppedNs = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, xmlMemberType);
-
- stillDoesntMatch = withDroppedNs != paramType;
- }
-
- if (stillDoesntMatch) {
- good = false;
- break;
- }
- }
-
- if (originalParamType != paramType)
- matchedMagicType = true;
- }
- if (!good) continue;
-
- if (MDocUpdater.SwitchingToMagicTypes && likelyCandidate == null && matchedMagicType) {
- // 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
- likelyCandidate = mi;
- continue;
- }
-
- return mi;
- }
-
- return likelyCandidate;
- }
-
- static string[] GetTypeParameters (string docName, IEnumerable<string> knownParameters)
- {
- if (docName [docName.Length-1] != '>')
- return null;
- StringList types = new StringList ();
- int endToken = docName.Length-2;
- int i = docName.Length-2;
- do {
- if (docName [i] == ',' || docName [i] == '<') {
- types.Add (docName.Substring (i + 1, endToken - i));
- endToken = i-1;
- }
- if (docName [i] == '<')
- break;
- } while (--i >= 0);
-
- types.Reverse ();
- var arrayTypes = types.ToArray ();
-
- if (knownParameters != null && knownParameters.Any () && arrayTypes.Length != knownParameters.Count ())
- return knownParameters.ToArray ();
- else
- return arrayTypes;
- }
-
- protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
- {
- // In case of dropping the namespace, we have to remove the dropped NS
- // so that docName will match what's in the assembly/type
- if (MDocUpdater.HasDroppedNamespace (type) && docName.StartsWith(MDocUpdater.droppedNamespace + ".")) {
- int droppedNsLength = MDocUpdater.droppedNamespace.Length;
- docName = docName.Substring (droppedNsLength + 1, docName.Length - droppedNsLength - 1);
- }
-
- // need to worry about 4 forms of //@MemberName values:
- // 1. "Normal" (non-generic) member names: GetEnumerator
- // - Lookup as-is.
- // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
- // - try as-is, and try type.member (due to "kludge" for property
- // support.
- // 3. "Normal" Generic member names: Sort<T> (CSC)
- // - need to remove generic parameters --> "Sort"
- // 4. Explicitly-implemented interface members for generic interfaces:
- // -- System.Collections.Generic.IEnumerable<T>.Current
- // - Try as-is, and try type.member, *keeping* the generic parameters.
- // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
- // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
- // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
- // this as (1) or (2).
- if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
- // Cases 1 & 2
- foreach (MemberReference mi in type.GetMembers (docName))
- yield return mi;
- if (CountChars(docName, '.') > 0) {
-
- Func<MemberReference, bool> verifyInterface = (member) =>
- {
- var meth = member as MethodDefinition;
-
- if (meth == null && member is PropertyReference) {
- var propertyDefinition = ((PropertyReference)member).Resolve ();
- meth = propertyDefinition.GetMethod ?? propertyDefinition.SetMethod;
- }
- return meth != null && (member.Name.Equals (".ctor") || DocUtils.IsExplicitlyImplemented (meth));
- };
-
- int memberCount = 0;
-
- // might be a property; try only type.member instead of
- // namespace.type.member.
- var typeMember = DocUtils.GetTypeDotMember (docName);
- var memberName = DocUtils.GetMember (docName);
- foreach (MemberReference mi in
- type.GetMembers (typeMember).Where (verifyInterface)) {
- memberCount++;
- yield return mi;
- }
-
- // some VB libraries use just the member name
- foreach (MemberReference mi in
- type.GetMembers (memberName).Where (verifyInterface)) {
- memberCount++;
- yield return mi;
- }
-
- // some VB libraries use a `typemember` naming convention
- foreach (MemberReference mi in
- type.GetMembers (typeMember.Replace(".", "")).Where (verifyInterface)) {
- memberCount++;
- yield return mi;
- }
-
- // if we still haven't found the member, there are some VB libraries
- // that use a different interface name for implementation.
- if (memberCount == 0) {
- foreach (MemberReference mi in
- type
- .GetMembers ()
- .Where (m => m.Name.StartsWith ("I", StringComparison.InvariantCultureIgnoreCase) && m.Name.EndsWith (memberName, StringComparison.InvariantCultureIgnoreCase))
- .Where (verifyInterface))
- yield return mi;
- }
- }
- yield break;
- }
- // cases 3 & 4
- int numLt = 0;
- int numDot = 0;
- int startLt, startType, startMethod;
- startLt = startType = startMethod = -1;
- for (int i = 0; i < docName.Length; ++i) {
- switch (docName [i]) {
- case '<':
- if (numLt == 0) {
- startLt = i;
- }
- ++numLt;
- break;
- case '>':
- --numLt;
- if (numLt == 0 && (i + 1) < docName.Length)
- // there's another character in docName, so this <...> sequence is
- // probably part of a generic type -- case 4.
- startLt = -1;
- break;
- case '.':
- startType = startMethod;
- startMethod = i;
- ++numDot;
- break;
- }
- }
- string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
- // case 3
- foreach (MemberReference mi in type.GetMembers (refName))
- yield return mi;
-
- // case 4
- foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
- yield return mi;
-
- // If we _still_ haven't found it, we've hit another generic naming issue:
- // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
- // explicitly-implemented METHOD names (not properties), e.g.
- // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
- // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
- // which the XML docs will contain.
- //
- // Alas, we can't derive the Mono name from docName, so we need to iterate
- // over all member names, convert them into CSC format, and compare... :-(
- if (numDot == 0)
- yield break;
- foreach (MemberReference mi in type.GetMembers ()) {
- if (MDocUpdater.GetMemberName (mi) == docName)
- yield return mi;
- }
- }
-
- static string GetReplacedString (string typeName, string[] from, string[] to)
- {
- if (from == null)
- return typeName;
- for (int i = 0; i < from.Length; ++i)
- typeName = typeName.Replace (from [i], to [i]);
- return typeName;
- }
-
- private static int CountChars (string s, char c)
- {
- int count = 0;
- for (int i = 0; i < s.Length; ++i) {
- if (s [i] == c)
- ++count;
- }
- return count;
- }
-}
-
-class EcmaDocumentationEnumerator : DocumentationEnumerator {
-
- XmlReader ecmadocs;
- MDocUpdater app;
-
- public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
- {
- this.app = app;
- this.ecmadocs = ecmaDocs;
- }
-
- public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
- {
- HashSet<string> seen = new HashSet<string> ();
- return GetDocumentationTypes (assembly, forTypes, seen)
- .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
- }
-
- new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
- {
- int typeDepth = -1;
- while (ecmadocs.Read ()) {
- switch (ecmadocs.Name) {
- case "Type": {
- if (typeDepth == -1)
- typeDepth = ecmadocs.Depth;
- if (ecmadocs.NodeType != XmlNodeType.Element)
- continue;
- if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
- continue;
- string typename = ecmadocs.GetAttribute ("FullName");
- string typename2 = MDocUpdater.GetTypeFileName (typename);
- if (forTypes != null &&
- forTypes.BinarySearch (typename) < 0 &&
- typename != typename2 &&
- forTypes.BinarySearch (typename2) < 0)
- continue;
- TypeDefinition t;
- if ((t = assembly.GetType (typename)) == null &&
- (t = assembly.GetType (typename2)) == null)
- continue;
- seen.Add (typename);
- if (typename != typename2)
- seen.Add (typename2);
- Console.WriteLine (" Import: {0}", t.FullName);
- if (ecmadocs.Name != "Docs") {
- int depth = ecmadocs.Depth;
- while (ecmadocs.Read ()) {
- if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
- break;
- }
- }
- if (!ecmadocs.IsStartElement ("Docs"))
- throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
- yield return t;
- break;
- }
- default:
- break;
- }
- }
- }
-
- public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
- {
- return GetMembers (basefile, type)
- .Concat (base.GetDocumentationMembers (basefile, type));
- }
-
- private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
- {
- while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
- // do nothing
- }
- if (ecmadocs.IsEmptyElement)
- yield break;
-
- int membersDepth = ecmadocs.Depth;
- bool go = true;
- while (go && ecmadocs.Read ()) {
- switch (ecmadocs.Name) {
- case "Member": {
- if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
- continue;
- DocumentationMember dm = new DocumentationMember (ecmadocs);
-
- string xp = MDocUpdater.GetXPathForMember (dm);
- XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
- MemberReference m;
- if (oldmember == null) {
- m = GetMember (type, dm);
- if (m == null) {
- app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
- type.FullName, dm.MemberSignatures ["C#"]);
- // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
- continue;
- }
- // oldmember lookup may have failed due to type parameter renames.
- // Try again.
- oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
- if (oldmember == null) {
- XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
- oldmember = basefile.CreateElement ("Member");
- oldmember.SetAttribute ("MemberName", dm.MemberName);
- members.AppendChild (oldmember);
- foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
- XmlElement ms = basefile.CreateElement ("MemberSignature");
- ms.SetAttribute ("Language", key);
- ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
- oldmember.AppendChild (ms);
- }
- oldmember.SetAttribute ("__monodocer-seen__", "true");
- Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
- app.additions++;
- }
- }
- else {
- m = GetMember (type, new DocumentationMember (oldmember));
- if (m == null) {
- app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
- type.FullName, dm.MemberSignatures ["C#"]);
- continue;
- }
- oldmember.SetAttribute ("__monodocer-seen__", "true");
- }
- DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
- if (ecmadocs.Name != "Docs")
- throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
- yield return node;
- break;
- }
- case "Members":
- if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
- go = false;
- }
- break;
- }
- }
- }
-}
-
-abstract class DocumentationImporter {
-
- public abstract void ImportDocumentation (DocsNodeInfo info);
-}
-
-class MsxdocDocumentationImporter : DocumentationImporter {
-
- XmlDocument slashdocs;
-
- public MsxdocDocumentationImporter (string file)
- {
- try
- {
- char oppositeSlash = Path.DirectorySeparatorChar == '/' ? '\\' : '/';
- if (file.Contains (oppositeSlash))
- file = file.Replace (oppositeSlash, Path.DirectorySeparatorChar);
-
- var xml = File.ReadAllText (file);
-
- // Ensure Unix line endings
- xml = xml.Replace ("\r", "");
-
- slashdocs = new XmlDocument ();
-
- slashdocs.LoadXml (xml);
- }
- catch (IOException ex)
- {
- Console.WriteLine ($"Importer Error: {ex.Message}");
- }
- }
-
- public override void ImportDocumentation (DocsNodeInfo info)
- {
- XmlNode elem = GetDocs (info.Member ?? info.Type);
-
- if (elem == null)
- return;
-
- XmlElement e = info.Node;
-
- if (elem.SelectSingleNode("summary") != null)
- MDocUpdater.ClearElement(e, "summary");
- if (elem.SelectSingleNode("remarks") != null)
- MDocUpdater.ClearElement(e, "remarks");
- if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
- MDocUpdater.ClearElement(e, "value");
- MDocUpdater.ClearElement(e, "returns");
- }
-
- foreach (XmlNode child in elem.ChildNodes) {
- switch (child.Name) {
- case "param":
- case "typeparam": {
- XmlAttribute name = child.Attributes ["name"];
- if (name == null)
- break;
- XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
- if (p2 != null)
- p2.InnerXml = child.InnerXml;
- break;
- }
- // Occasionally XML documentation will use <returns/> on
- // properties, so let's try to normalize things.
- case "value":
- case "returns": {
- XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
- v.InnerXml = child.InnerXml;
- e.AppendChild (v);
- break;
- }
- case "altmember":
- case "exception":
- case "permission": {
- XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
- if (cref == null)
- break;
- XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
- if (a == null) {
- a = e.OwnerDocument.CreateElement (child.Name);
- a.SetAttribute ("cref", cref.Value);
- e.AppendChild (a);
- }
- a.InnerXml = child.InnerXml;
- break;
- }
- case "seealso": {
- XmlAttribute cref = child.Attributes ["cref"];
- if (cref == null)
- break;
- XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
- if (a == null) {
- a = e.OwnerDocument.CreateElement ("altmember");
- a.SetAttribute ("cref", cref.Value);
- e.AppendChild (a);
- }
- break;
- }
- default: {
- bool add = true;
- if (child.NodeType == XmlNodeType.Element &&
- e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
- add = false;
- if (add)
- MDocUpdater.CopyNode (child, e);
- break;
- }
- }
- }
- }
-
- private XmlNode GetDocs (MemberReference member)
- {
- string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
- if (slashdocsig != null && slashdocs != null)
- return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
- return null;
- }
-}
-
-class EcmaDocumentationImporter : DocumentationImporter {
-
- XmlReader ecmadocs;
-
- public EcmaDocumentationImporter (XmlReader ecmaDocs)
- {
- this.ecmadocs = ecmaDocs;
- }
-
- public override void ImportDocumentation (DocsNodeInfo info)
- {
- if (!ecmadocs.IsStartElement ("Docs")) {
- return;
- }
-
- XmlElement e = info.Node;
-
- int depth = ecmadocs.Depth;
- ecmadocs.ReadStartElement ("Docs");
- while (ecmadocs.Read ()) {
- if (ecmadocs.Name == "Docs") {
- if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
- break;
- else
- throw new InvalidOperationException ("Skipped past current <Docs/> element!");
- }
- if (!ecmadocs.IsStartElement ())
- continue;
- switch (ecmadocs.Name) {
- case "param":
- case "typeparam": {
- string name = ecmadocs.GetAttribute ("name");
- if (name == null)
- break;
- XmlNode doc = e.SelectSingleNode (
- ecmadocs.Name + "[@name='" + name + "']");
- string value = ecmadocs.ReadInnerXml ();
- if (doc != null)
- doc.InnerXml = value.Replace ("\r", "");
- break;
- }
- case "altmember":
- case "exception":
- case "permission":
- case "seealso": {
- string name = ecmadocs.Name;
- string cref = ecmadocs.GetAttribute ("cref");
- if (cref == null)
- break;
- XmlNode doc = e.SelectSingleNode (
- ecmadocs.Name + "[@cref='" + cref + "']");
- string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
- if (doc != null)
- doc.InnerXml = value;
- else {
- XmlElement n = e.OwnerDocument.CreateElement (name);
- n.SetAttribute ("cref", cref);
- n.InnerXml = value;
- e.AppendChild (n);
- }
- break;
- }
- default: {
- string name = ecmadocs.Name;
- string xpath = ecmadocs.Name;
- StringList attributes = new StringList (ecmadocs.AttributeCount);
- if (ecmadocs.MoveToFirstAttribute ()) {
- do {
- attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
- } while (ecmadocs.MoveToNextAttribute ());
- ecmadocs.MoveToContent ();
- }
- if (attributes.Count > 0) {
- xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
- }
- XmlNode doc = e.SelectSingleNode (xpath);
- string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
- if (doc != null) {
- doc.InnerXml = value;
- }
- else {
- XmlElement n = e.OwnerDocument.CreateElement (name);
- n.InnerXml = value;
- foreach (string a in attributes) {
- int eq = a.IndexOf ('=');
- n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
- }
- e.AppendChild (n);
- }
- break;
- }
- }
- }
- }
-}
-
-class DocumentationMember {
- public StringToStringMap MemberSignatures = new StringToStringMap ();
- public string ReturnType;
- public StringList Parameters;
- public StringList TypeParameters;
- public string MemberName;
- public string MemberType;
-
- /// <summary>Removes modreq and modopt from ReturnType, Parameters, and TypeParameters</summary>
- private void CleanTypes() {
- ReturnType = MemberFormatter.RemoveMod (ReturnType);
- MemberType = MemberFormatter.RemoveMod (MemberType);
-
- if (Parameters != null)
- {
- for (var i = 0; i < Parameters.Count; i++)
- Parameters[i] = MemberFormatter.RemoveMod (Parameters[i]);
- }
-
- if (TypeParameters != null)
- {
- for (var i = 0; i < TypeParameters.Count; i++)
- TypeParameters[i] = MemberFormatter.RemoveMod (TypeParameters[i]);
- }
- }
-
- public DocumentationMember (XmlReader reader)
- {
- MemberName = reader.GetAttribute ("MemberName");
- int depth = reader.Depth;
- bool go = true;
- StringList p = new StringList ();
- StringList tp = new StringList ();
- do {
- if (reader.NodeType != XmlNodeType.Element)
- continue;
-
- bool shouldUse = true;
- try {
- string apistyle = reader.GetAttribute ("apistyle");
- shouldUse = string.IsNullOrWhiteSpace(apistyle) || apistyle == "classic"; // only use this tag if it's an 'classic' style node
- }
- catch (Exception ex) {}
- switch (reader.Name) {
- case "MemberSignature":
- if (shouldUse) {
- MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
- }
- break;
- case "MemberType":
- MemberType = reader.ReadElementString ();
- break;
- case "ReturnType":
- if (reader.Depth == depth + 2 && shouldUse)
- ReturnType = reader.ReadElementString ();
- break;
- case "Parameter":
- if (reader.Depth == depth + 2 && shouldUse)
- p.Add (reader.GetAttribute ("Type"));
- break;
- case "TypeParameter":
- if (reader.Depth == depth + 2 && shouldUse)
- tp.Add (reader.GetAttribute ("Name"));
- break;
- case "Docs":
- if (reader.Depth == depth + 1)
- go = false;
- break;
- }
- } while (go && reader.Read () && reader.Depth >= depth);
- if (p.Count > 0) {
- Parameters = p;
- }
- if (tp.Count > 0) {
- TypeParameters = tp;
- } else {
- DiscernTypeParameters ();
- }
-
- CleanTypes ();
- }
-
- public DocumentationMember (XmlNode node)
- {
- MemberName = node.Attributes ["MemberName"].Value;
- foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
- XmlAttribute l = n.Attributes ["Language"];
- XmlAttribute v = n.Attributes ["Value"];
- XmlAttribute apistyle = n.Attributes ["apistyle"];
- bool shouldUse = apistyle == null || apistyle.Value == "classic";
- if (l != null && v != null && shouldUse)
- MemberSignatures [l.Value] = v.Value;
- }
- MemberType = node.SelectSingleNode ("MemberType").InnerText;
- XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType[not(@apistyle) or @apistyle='classic']");
- if (rt != null)
- ReturnType = rt.InnerText;
- XmlNodeList p = node.SelectNodes ("Parameters/Parameter[not(@apistyle) or @apistyle='classic']");
- if (p.Count > 0) {
- Parameters = new StringList (p.Count);
- for (int i = 0; i < p.Count; ++i)
- Parameters.Add (p [i].Attributes ["Type"].Value);
- }
- XmlNodeList tp = node.SelectNodes ("TypeParameters/TypeParameter[not(@apistyle) or @apistyle='classic']");
- if (tp.Count > 0) {
- TypeParameters = new StringList (tp.Count);
- for (int i = 0; i < tp.Count; ++i)
- TypeParameters.Add (tp [i].Attributes ["Name"].Value);
- }
- else {
- DiscernTypeParameters ();
- }
-
- CleanTypes ();
- }
-
- void DiscernTypeParameters ()
- {
- // see if we can discern the param list from the name
- if (MemberName.Contains ("<") && MemberName.EndsWith (">")) {
- var starti = MemberName.IndexOf ("<") + 1;
- var endi = MemberName.LastIndexOf (">");
- var paramlist = MemberName.Substring (starti, endi - starti);
- var tparams = paramlist.Split (new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
- TypeParameters = new StringList (tparams);
- }
- }
-}
-
-public class DynamicParserContext {
- public ReadOnlyCollection<bool> TransformFlags;
- public int TransformIndex;
-
- public DynamicParserContext (ICustomAttributeProvider provider)
- {
- CustomAttribute da;
- if (provider.HasCustomAttributes &&
- (da = (provider.CustomAttributes.Cast<CustomAttribute>()
- .SingleOrDefault (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))) != null) {
- CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
- ? new CustomAttributeArgument [0]
- : (CustomAttributeArgument[]) da.ConstructorArguments [0].Value;
-
- TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool) t.Value).ToArray());
- }
- }
-}
-
-public enum MemberFormatterState {
- None,
- WithinGenericTypeParameters,
-}
-
-public abstract class MemberFormatter {
-
- public virtual string Language {
- get {return "";}
- }
-
- public string GetName (MemberReference member)
- {
- return GetName (member, null);
- }
-
- public virtual string GetName (MemberReference member, DynamicParserContext context)
- {
- TypeReference type = member as TypeReference;
- if (type != null)
- return GetTypeName (type, context);
- MethodReference method = member as MethodReference;
- if (method != null && method.Name == ".ctor") // method.IsConstructor
- return GetConstructorName (method);
- if (method != null)
- return GetMethodName (method);
- PropertyReference prop = member as PropertyReference;
- if (prop != null)
- return GetPropertyName (prop);
- FieldReference field = member as FieldReference;
- if (field != null)
- return GetFieldName (field);
- EventReference e = member as EventReference;
- if (e != null)
- return GetEventName (e);
- throw new NotSupportedException ("Can't handle: " +
- (member == null ? "null" : member.GetType().ToString()));
- }
-
- protected virtual string GetTypeName (TypeReference type)
- {
- return GetTypeName (type, null);
- }
-
- protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
- {
- if (type == null)
- throw new ArgumentNullException (nameof (type));
-
- var typeName = _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
-
- typeName = RemoveMod (typeName);
-
- return typeName;
- }
-
- public static string RemoveMod (string typeName)
- {
- if (string.IsNullOrWhiteSpace (typeName)) return typeName;
-
- // For custom modifiers (modopt/modreq), we are just excising them
- // via string manipulation for simplicity; since these cannot be
- // expressed in C#. If finer control is needed in the future, you can
- // use IModifierType, PointerType, ByReferenceType, etc.
-
- int modIndex = Math.Max (typeName.LastIndexOf ("modopt(", StringComparison.Ordinal), typeName.LastIndexOf ("modreq(", StringComparison.Ordinal));
- if (modIndex > 0)
- {
- var tname = typeName.Substring (0, modIndex - 1);
- var parenIndex = typeName.LastIndexOf (')');
- if (parenIndex == typeName.Length - 2)
- { // see if there's metadata like a pointer
- tname += typeName.Last ();
- }
- typeName = tname;
- }
-
- modIndex = Math.Max (typeName.LastIndexOf ("modopt(", StringComparison.Ordinal), typeName.LastIndexOf ("modreq(", StringComparison.Ordinal));
- if (modIndex >= 0)
- return RemoveMod (typeName);
- else
- return typeName;
- }
-
- protected virtual char[] ArrayDelimeters {
- get {return new char[]{'[', ']'};}
- }
-
- protected virtual MemberFormatterState MemberFormatterState { get; set; }
-
- protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
- {
- if (type is ArrayType) {
- TypeSpecification spec = type as TypeSpecification;
- _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
- return AppendArrayModifiers (buf, (ArrayType) type);
- }
- if (type is ByReferenceType) {
- return AppendRefTypeName (buf, type, context);
- }
- if (type is PointerType) {
- return AppendPointerTypeName (buf, type, context);
- }
- if (type is GenericParameter) {
- return AppendTypeName (buf, type, context);
- }
- AppendNamespace (buf, type);
- GenericInstanceType genInst = type as GenericInstanceType;
- if (type.GenericParameters.Count == 0 &&
- (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
- return AppendFullTypeName (buf, type, context);
- }
- return AppendGenericType (buf, type, context);
- }
-
- protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
- {
- string ns = DocUtils.GetNamespace (type);
- if (ns != null && ns.Length > 0)
- buf.Append (ns).Append ('.');
- return buf;
- }
-
- protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
- {
- if (type.DeclaringType != null)
- AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
- return AppendTypeName (buf, type, context);
- }
-
- protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
- {
- if (context != null)
- context.TransformIndex++;
- return AppendTypeName (buf, type.Name);
- }
-
- protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
- {
- int n = typename.IndexOf ("`");
- if (n >= 0)
- return buf.Append (typename.Substring (0, n));
- return buf.Append (typename);
- }
-
- protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
- {
- buf.Append (ArrayDelimeters [0]);
- int rank = array.Rank;
- if (rank > 1)
- buf.Append (new string (',', rank-1));
- return buf.Append (ArrayDelimeters [1]);
- }
-
- protected virtual string RefTypeModifier {
- get {return "@";}
- }
-
- protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
- {
- TypeSpecification spec = type as TypeSpecification;
- return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
- .Append (RefTypeModifier);
- }
-
- protected virtual string PointerModifier {
- get {return "*";}
- }
-
- protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
- {
- TypeSpecification spec = type as TypeSpecification;
- return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
- .Append (PointerModifier);
- }
-
- protected virtual char[] GenericTypeContainer {
- get {return new char[]{'<', '>'};}
- }
-
- protected virtual char NestedTypeSeparator {
- get {return '.';}
- }
-
- protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
- {
- List<TypeReference> decls = DocUtils.GetDeclaringTypes (
- type is GenericInstanceType ? type.GetElementType () : type);
- List<TypeReference> genArgs = GetGenericArguments (type);
- int argIdx = 0;
- int prev = 0;
- bool insertNested = false;
- foreach (var decl in decls) {
- TypeReference declDef = decl.Resolve () ?? decl;
- if (insertNested) {
- buf.Append (NestedTypeSeparator);
- }
- insertNested = true;
- AppendTypeName (buf, declDef, context);
- int ac = DocUtils.GetGenericArgumentCount (declDef);
- int c = ac - prev;
- prev = ac;
- if (c > 0) {
- buf.Append (GenericTypeContainer [0]);
- var origState = MemberFormatterState;
- MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
- _AppendTypeName (buf, genArgs [argIdx++], context);
- for (int i = 1; i < c; ++i) {
- _AppendTypeName (buf.Append (","), genArgs [argIdx++], context);
- }
- MemberFormatterState = origState;
- buf.Append (GenericTypeContainer [1]);
- }
- }
- return buf;
- }
-
- protected List<TypeReference> GetGenericArguments (TypeReference type)
- {
- var args = new List<TypeReference> ();
- GenericInstanceType inst = type as GenericInstanceType;
- if (inst != null)
- args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
- else
- args.AddRange (type.GenericParameters.Cast<TypeReference> ());
- return args;
- }
-
- protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
- {
- return buf;
- }
-
- protected virtual string GetConstructorName (MethodReference constructor)
- {
- return constructor.Name;
- }
-
- protected virtual string GetMethodName (MethodReference method)
- {
- return method.Name;
- }
-
- protected virtual string GetPropertyName (PropertyReference property)
- {
- return property.Name;
- }
-
- protected virtual string GetFieldName (FieldReference field)
- {
- return field.Name;
- }
-
- protected virtual string GetEventName (EventReference e)
- {
- return e.Name;
- }
-
- public virtual string GetDeclaration (TypeReference tref)
- {
- var typeSpec = tref as TypeSpecification;
- if (typeSpec != null && typeSpec.Resolve () == null && typeSpec.IsArray && typeSpec.ContainsGenericParameter) {
- //HACK: there's really no good reference for a generic parameter array, so we're going to use object
- return "T:System.Array";
- }
- TypeDefinition def = tref.Resolve ();
- if (def != null)
- return GetTypeDeclaration (def);
- else
- return GetTypeName (tref);
- }
-
- public virtual string GetDeclaration (MemberReference mreference)
- {
- return GetDeclaration (mreference.Resolve ());
- }
-
- string GetDeclaration (IMemberDefinition member)
- {
- if (member == null)
- throw new ArgumentNullException ("member");
- TypeDefinition type = member as TypeDefinition;
- if (type != null)
- return GetTypeDeclaration (type);
- MethodDefinition method = member as MethodDefinition;
- if (method != null && method.IsConstructor)
- return GetConstructorDeclaration (method);
- if (method != null)
- return GetMethodDeclaration (method);
- PropertyDefinition prop = member as PropertyDefinition;
- if (prop != null)
- return GetPropertyDeclaration (prop);
- FieldDefinition field = member as FieldDefinition;
- if (field != null)
- return GetFieldDeclaration (field);
- EventDefinition e = member as EventDefinition;
- if (e != null)
- return GetEventDeclaration (e);
- throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
- }
-
- protected virtual string GetTypeDeclaration (TypeDefinition type)
- {
- if (type == null)
- throw new ArgumentNullException ("type");
- StringBuilder buf = new StringBuilder (type.Name.Length);
- _AppendTypeName (buf, type, null);
- AppendGenericTypeConstraints (buf, type);
- return buf.ToString ();
- }
-
- protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
- {
- return GetConstructorName (constructor);
- }
-
- protected virtual string GetMethodDeclaration (MethodDefinition method)
- {
- if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
- ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
- return null;
-
- // Special signature for destructors.
- if (method.Name == "Finalize" && method.Parameters.Count == 0)
- return GetFinalizerName (method);
-
- StringBuilder buf = new StringBuilder ();
-
- AppendVisibility (buf, method);
- if (buf.Length == 0 &&
- !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
- return null;
-
- AppendModifiers (buf, method);
-
- if (buf.Length != 0)
- buf.Append (" ");
-
- buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
-
- AppendMethodName (buf, method);
- AppendGenericMethod (buf, method).Append (" ");
- AppendParameters (buf, method, method.Parameters);
- AppendGenericMethodConstraints (buf, method);
- return buf.ToString ();
- }
-
- protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
- {
- return buf.Append (method.Name);
- }
-
- protected virtual string GetFinalizerName (MethodDefinition method)
- {
- return "Finalize";
- }
-
- protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
- {
- return buf;
- }
-
- protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
- {
- return buf;
- }
-
- protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
- {
- return buf;
- }
-
- protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
- {
- return buf;
- }
-
- protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
- {
- return buf;
- }
-
- protected virtual string GetPropertyDeclaration (PropertyDefinition property)
- {
- return GetPropertyName (property);
- }
-
- protected virtual string GetFieldDeclaration (FieldDefinition field)
- {
- return GetFieldName (field);
- }
-
- protected virtual string GetEventDeclaration (EventDefinition e)
- {
- return GetEventName (e);
- }
-}
-
-class DocIdFormatter : MemberFormatter
-{
- public override string Language
- {
- get { return "DocId"; }
- }
-
- public override string GetDeclaration (TypeReference tref)
- {
- return DocCommentId.GetDocCommentId (tref.Resolve ());
- }
- public override string GetDeclaration (MemberReference mreference)
- {
- return DocCommentId.GetDocCommentId (mreference.Resolve ());
- }
-}
-
-public class ILFullMemberFormatter : MemberFormatter {
-
- public override string Language {
- get {return "ILAsm";}
- }
-
- protected override char NestedTypeSeparator {
- get {
- return '/';
- }
- }
-
- protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
- {
- if (GetBuiltinType (type.FullName) != null)
- return buf;
- string ns = DocUtils.GetNamespace (type);
- if (ns != null && ns.Length > 0) {
- if (type.IsValueType)
- buf.Append ("valuetype ");
- else
- buf.Append ("class ");
- buf.Append (ns).Append ('.');
- }
- return buf;
- }
-
- protected static string GetBuiltinType (string t)
- {
- switch (t) {
- case "System.Byte": return "unsigned int8";
- case "System.SByte": return "int8";
- case "System.Int16": return "int16";
- case "System.Int32": return "int32";
- case "System.Int64": return "int64";
- case "System.IntPtr": return "native int";
-
- case "System.UInt16": return "unsigned int16";
- case "System.UInt32": return "unsigned int32";
- case "System.UInt64": return "unsigned int64";
- case "System.UIntPtr": return "native unsigned int";
-
- case "System.Single": return "float32";
- case "System.Double": return "float64";
- case "System.Boolean": return "bool";
- case "System.Char": return "char";
- case "System.Void": return "void";
- case "System.String": return "string";
- case "System.Object": return "object";
- }
- return null;
- }
-
- protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
- {
- return buf.Append (typename);
- }
-
- protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
- {
- if (type is GenericParameter) {
- AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
- return buf;
- }
-
- string s = GetBuiltinType (type.FullName);
- if (s != null) {
- return buf.Append (s);
- }
- return base.AppendTypeName (buf, type, context);
- }
-
- private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
- {
- if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
- return buf.Append (type.Owner is TypeReference ? "!" : "!!");
- }
- GenericParameterAttributes attrs = type.Attributes;
- if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
- buf.Append ("class ");
- if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
- buf.Append ("struct ");
- if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
- buf.Append (".ctor ");
- IList<TypeReference> constraints = type.Constraints;
- MemberFormatterState = 0;
- if (constraints.Count > 0) {
- var full = new ILFullMemberFormatter ();
- buf.Append ("(").Append (full.GetName (constraints [0]));
- for (int i = 1; i < constraints.Count; ++i) {
- buf.Append (", ").Append (full.GetName (constraints [i]));
- }
- buf.Append (") ");
- }
- MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
-
- if ((attrs & GenericParameterAttributes.Covariant) != 0)
- buf.Append ("+ ");
- if ((attrs & GenericParameterAttributes.Contravariant) != 0)
- buf.Append ("- ");
- return buf;
- }
-
- protected override string GetTypeDeclaration (TypeDefinition type)
- {
- string visibility = GetTypeVisibility (type.Attributes);
- if (visibility == null)
- return null;
-
- StringBuilder buf = new StringBuilder ();
-
- buf.Append (".class ");
- if (type.IsNested)
- buf.Append ("nested ");
- buf.Append (visibility).Append (" ");
- if (type.IsInterface)
- buf.Append ("interface ");
- if (type.IsSequentialLayout)
- buf.Append ("sequential ");
- if (type.IsAutoLayout)
- buf.Append ("auto ");
- if (type.IsAnsiClass)
- buf.Append ("ansi ");
- if (type.IsAbstract)
- buf.Append ("abstract ");
- if (type.IsSerializable)
- buf.Append ("serializable ");
- if (type.IsSealed)
- buf.Append ("sealed ");
- if (type.IsBeforeFieldInit)
- buf.Append ("beforefieldinit ");
- var state = MemberFormatterState;
- MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
- buf.Append (GetName (type));
- MemberFormatterState = state;
- var full = new ILFullMemberFormatter ();
- if (type.BaseType != null) {
- buf.Append (" extends ");
- if (type.BaseType.FullName == "System.Object")
- buf.Append ("System.Object");
- else
- buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
- }
- bool first = true;
- foreach (var name in type.Interfaces.Where (i => MDocUpdater.IsPublic (i.InterfaceType.Resolve ()))
- .Select (i => full.GetName (i.InterfaceType))
- .OrderBy (n => n)) {
- if (first) {
- buf.Append (" implements ");
- first = false;
- }
- else {
- buf.Append (", ");
- }
- buf.Append (name);
- }
-
- return buf.ToString ();
- }
-
- protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
- {
- List<TypeReference> decls = DocUtils.GetDeclaringTypes (
- type is GenericInstanceType ? type.GetElementType () : type);
- bool first = true;
- foreach (var decl in decls) {
- TypeReference declDef = decl.Resolve () ?? decl;
- if (!first) {
- buf.Append (NestedTypeSeparator);
- }
- first = false;
- AppendTypeName (buf, declDef, context);
- }
- buf.Append ('<');
- first = true;
- foreach (TypeReference arg in GetGenericArguments (type)) {
- if (!first)
- buf.Append (", ");
- first = false;
- _AppendTypeName (buf, arg, context);
- }
- buf.Append ('>');
- return buf;
- }
-
- static string GetTypeVisibility (TypeAttributes ta)
- {
- switch (ta & TypeAttributes.VisibilityMask) {
- case TypeAttributes.Public:
- case TypeAttributes.NestedPublic:
- return "public";
-
- case TypeAttributes.NestedFamily:
- case TypeAttributes.NestedFamORAssem:
- return "protected";
-
- default:
- return null;
- }
- }
-
- protected override string GetConstructorDeclaration (MethodDefinition constructor)
- {
- return GetMethodDeclaration (constructor);
- }
-
- protected override string GetMethodDeclaration (MethodDefinition method)
- {
- if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
- return null;
-
- var buf = new StringBuilder ();
- buf.Append (".method ");
- AppendVisibility (buf, method);
- if (method.IsStatic)
- buf.Append ("static ");
- if (method.IsHideBySig)
- buf.Append ("hidebysig ");
- if (method.IsPInvokeImpl) {
- var info = method.PInvokeInfo;
- buf.Append ("pinvokeimpl (\"")
- .Append (info.Module.Name)
- .Append ("\" as \"")
- .Append (info.EntryPoint)
- .Append ("\"");
- if (info.IsCharSetAuto)
- buf.Append (" auto");
- if (info.IsCharSetUnicode)
- buf.Append (" unicode");
- if (info.IsCharSetAnsi)
- buf.Append (" ansi");
- if (info.IsCallConvCdecl)
- buf.Append (" cdecl");
- if (info.IsCallConvStdCall)
- buf.Append (" stdcall");
- if (info.IsCallConvWinapi)
- buf.Append (" winapi");
- if (info.IsCallConvThiscall)
- buf.Append (" thiscall");
- if (info.SupportsLastError)
- buf.Append (" lasterr");
- buf.Append (")");
- }
- if (method.IsSpecialName)
- buf.Append ("specialname ");
- if (method.IsRuntimeSpecialName)
- buf.Append ("rtspecialname ");
- if (method.IsNewSlot)
- buf.Append ("newslot ");
- if (method.IsVirtual)
- buf.Append ("virtual ");
- if (!method.IsStatic)
- buf.Append ("instance ");
- _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
- buf.Append (' ')
- .Append (method.Name);
- if (method.IsGenericMethod ()) {
- var state = MemberFormatterState;
- MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
- IList<GenericParameter> args = method.GenericParameters;
- if (args.Count > 0) {
- buf.Append ("<");
- _AppendTypeName (buf, args [0], null);
- for (int i = 1; i < args.Count; ++i)
- _AppendTypeName (buf.Append (", "), args [i], null);
- buf.Append (">");
- }
- MemberFormatterState = state;
- }
-
- buf.Append ('(');
- bool first = true;
- for (int i = 0; i < method.Parameters.Count; ++i) {
- var param = method.Parameters[i];
- if (!first)
- buf.Append (", ");
- first = false;
-
- if (param.IsOut) buf.Append ("[out] ");
- else if (param.IsIn) buf.Append ("[in]");
-
- _AppendTypeName (buf, param.ParameterType, new DynamicParserContext (param));
- if (param.ParameterType.IsByReference) buf.Append ("&");
- buf.Append (' ');
- buf.Append (param.Name);
- }
- buf.Append (')');
- if (method.IsIL)
- buf.Append (" cil");
- if (method.IsRuntime)
- buf.Append (" runtime");
- if (method.IsManaged)
- buf.Append (" managed");
-
- return buf.ToString ();
- }
-
- protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
- {
- if (DocUtils.IsExplicitlyImplemented (method)) {
- TypeReference iface;
- MethodReference ifaceMethod;
- DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
- return buf.Append (new CSharpMemberFormatter ().GetName (iface))
- .Append ('.')
- .Append (ifaceMethod.Name);
- }
- return base.AppendMethodName (buf, method);
- }
-
- protected override string RefTypeModifier {
- get {return "";}
- }
-
- protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
- {
- if (method.IsPublic)
- return buf.Append ("public ");
- if (method.IsFamilyAndAssembly)
- return buf.Append ("familyandassembly");
- if (method.IsFamilyOrAssembly)
- return buf.Append ("familyorassembly");
- if (method.IsFamily)
- return buf.Append ("family");
- return buf;
- }
-
- protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
- {
- string modifiers = String.Empty;
- if (method.IsStatic) modifiers += " static";
- if (method.IsVirtual && !method.IsAbstract) {
- if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
- else modifiers += " override";
- }
- TypeDefinition declType = (TypeDefinition) method.DeclaringType;
- if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
- if (method.IsFinal) modifiers += " sealed";
- if (modifiers == " virtual sealed") modifiers = "";
-
- return buf.Append (modifiers);
- }
-
- protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
- {
- if (method.IsGenericMethod ()) {
- IList<GenericParameter> args = method.GenericParameters;
- if (args.Count > 0) {
- buf.Append ("<");
- buf.Append (args [0].Name);
- for (int i = 1; i < args.Count; ++i)
- buf.Append (",").Append (args [i].Name);
- buf.Append (">");
- }
- }
- return buf;
- }
-
- protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
- {
- return AppendParameters (buf, method, parameters, '(', ')');
- }
-
- private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
- {
- buf.Append (begin);
-
- if (parameters.Count > 0) {
- if (DocUtils.IsExtensionMethod (method))
- buf.Append ("this ");
- AppendParameter (buf, parameters [0]);
- for (int i = 1; i < parameters.Count; ++i) {
- buf.Append (", ");
- AppendParameter (buf, parameters [i]);
- }
- }
-
- return buf.Append (end);
- }
-
- private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
- {
- if (parameter.ParameterType is ByReferenceType) {
- if (parameter.IsOut)
- buf.Append ("out ");
- else
- buf.Append ("ref ");
- }
- buf.Append (GetName (parameter.ParameterType)).Append (" ");
- return buf.Append (parameter.Name);
- }
-
- protected override string GetPropertyDeclaration (PropertyDefinition property)
- {
- MethodDefinition gm = null, sm = null;
-
- string get_visible = null;
- if ((gm = property.GetMethod) != null &&
- (DocUtils.IsExplicitlyImplemented (gm) ||
- (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
- get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
- string set_visible = null;
- if ((sm = property.SetMethod) != null &&
- (DocUtils.IsExplicitlyImplemented (sm) ||
- (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
- set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
-
- if ((set_visible == null) && (get_visible == null))
- return null;
-
- StringBuilder buf = new StringBuilder ()
- .Append (".property ");
- if (!(gm ?? sm).IsStatic)
- buf.Append ("instance ");
- _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
- buf.Append (' ').Append (property.Name);
- if (!property.HasParameters || property.Parameters.Count == 0)
- return buf.ToString ();
-
- buf.Append ('(');
- bool first = true;
- foreach (ParameterDefinition p in property.Parameters) {
- if (!first)
- buf.Append (", ");
- first = false;
- _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
- }
- buf.Append (')');
-
- return buf.ToString ();
- }
-
- protected override string GetFieldDeclaration (FieldDefinition field)
- {
- TypeDefinition declType = (TypeDefinition) field.DeclaringType;
- if (declType.IsEnum && field.Name == "value__")
- return null; // This member of enums aren't documented.
-
- StringBuilder buf = new StringBuilder ();
- AppendFieldVisibility (buf, field);
- if (buf.Length == 0)
- return null;
-
- buf.Insert (0, ".field ");
-
- if (field.IsStatic)
- buf.Append ("static ");
- if (field.IsInitOnly)
- buf.Append ("initonly ");
- if (field.IsLiteral)
- buf.Append ("literal ");
- _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
- buf.Append (' ').Append (field.Name);
- AppendFieldValue (buf, field);
-
- return buf.ToString ();
- }
-
- static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
- {
- if (field.IsPublic)
- return buf.Append ("public ");
- if (field.IsFamilyAndAssembly)
- return buf.Append ("familyandassembly ");
- if (field.IsFamilyOrAssembly)
- return buf.Append ("familyorassembly ");
- if (field.IsFamily)
- return buf.Append ("family ");
- return buf;
- }
-
- static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
- {
- // enums have a value__ field, which we ignore
- if (field.DeclaringType.IsGenericType ())
- return buf;
- if (field.HasConstant && field.IsLiteral) {
- object val = null;
- try {
- val = field.Constant;
- } catch {
- return buf;
- }
- if (val == null)
- buf.Append (" = ").Append ("null");
- else if (val is Enum)
- buf.Append (" = ")
- .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
- .Append ('(')
- .Append (val.ToString ())
- .Append (')');
- else if (val is IFormattable) {
- string value = ((IFormattable)val).ToString(null, CultureInfo.InvariantCulture);
- buf.Append (" = ");
- if (val is string)
- buf.Append ("\"" + value + "\"");
- else
- buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
- .Append ('(')
- .Append (value)
- .Append (')');
- }
- }
- return buf;
- }
-
- protected override string GetEventDeclaration (EventDefinition e)
- {
- StringBuilder buf = new StringBuilder ();
- if (AppendVisibility (buf, e.AddMethod).Length == 0) {
- return null;
- }
-
- buf.Length = 0;
- buf.Append (".event ")
- .Append (GetName (e.EventType))
- .Append (' ')
- .Append (e.Name);
-
- return buf.ToString ();
- }
-}
-
-public class ILMemberFormatter : ILFullMemberFormatter {
- protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
- {
- return buf;
- }
-}
-
- class ILNativeTypeMemberFormatter : ILFullMemberFormatter {
- protected static string _GetBuiltinType (string t)
- {
- //string moddedType = base.GetBuiltinType (t);
- return null;
- //return moddedType;
- }
- }
-
- class CSharpNativeTypeMemberFormatter : CSharpFullMemberFormatter {
- protected override string GetCSharpType (string t) {
- string moddedType = base.GetCSharpType (t);
-
- switch (moddedType) {
- case "int": return "nint";
- case "uint":
- return "nuint";
- case "float":
- return "nfloat";
- case "System.Drawing.SizeF":
- return "CoreGraphics.CGSize";
- case "System.Drawing.PointF":
- return "CoreGraphics.CGPoint";
- case "System.Drawing.RectangleF":
- return "CoreGraphics.CGPoint";
- }
- return null;
- }
- }
-
-public class CSharpFullMemberFormatter : MemberFormatter {
-
- public override string Language {
- get {return "C#";}
- }
-
- protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
- {
-
- string ns = DocUtils.GetNamespace (type);
- if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
- buf.Append (ns).Append ('.');
- return buf;
- }
-
- protected virtual string GetCSharpType (string t)
- {
- // make sure there are no modifiers in the type string (add them back before returning)
- string typeToCompare = t;
- string[] splitType = null;
- if (t.Contains (' ')) {
- splitType = t.Split (' ');
- typeToCompare = splitType[0];
- }
-
- switch (typeToCompare) {
- case "System.Byte": typeToCompare = "byte"; break;
- case "System.SByte": typeToCompare = "sbyte"; break;
- case "System.Int16": typeToCompare = "short"; break;
- case "System.Int32": typeToCompare = "int"; break;
- case "System.Int64": typeToCompare = "long"; break;
-
- case "System.UInt16": typeToCompare = "ushort"; break;
- case "System.UInt32": typeToCompare = "uint"; break;
- case "System.UInt64": typeToCompare = "ulong"; break;
-
- case "System.Single": typeToCompare = "float"; break;
- case "System.Double": typeToCompare = "double"; break;
- case "System.Decimal": typeToCompare = "decimal"; break;
- case "System.Boolean": typeToCompare = "bool"; break;
- case "System.Char": typeToCompare = "char"; break;
- case "System.Void": typeToCompare = "void"; break;
- case "System.String": typeToCompare = "string"; break;
- case "System.Object": typeToCompare = "object"; break;
- }
-
- if (splitType != null) {
- // re-add modreq/modopt if it was there
- splitType[0] = typeToCompare;
- typeToCompare = string.Join (" ", splitType);
- }
- return typeToCompare == t ? null : typeToCompare;
- }
-
- protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
- {
- if (context != null && context.TransformFlags != null &&
- (context.TransformFlags.Count == 0 || context.TransformFlags [context.TransformIndex])) {
- context.TransformIndex++;
- return buf.Append ("dynamic");
- }
-
- if (type is GenericParameter)
- return AppendGenericParameterConstraints (buf, (GenericParameter) type, context).Append (type.Name);
- string t = type.FullName;
- if (!t.StartsWith ("System.")) {
- return base.AppendTypeName (buf, type, context);
- }
-
- string s = GetCSharpType (t);
- if (s != null) {
- if (context != null)
- context.TransformIndex++;
- return buf.Append (s);
- }
-
- return base.AppendTypeName (buf, type, context);
- }
-
- private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
- {
- if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
- return buf;
- GenericParameterAttributes attrs = type.Attributes;
- bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
- bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
- if (isin)
- buf.Append ("in ");
- else if (isout)
- buf.Append ("out ");
- return buf;
- }
-
- protected override string GetTypeDeclaration (TypeDefinition type)
- {
- string visibility = GetTypeVisibility (type.Attributes);
- if (visibility == null)
- return null;
-
- StringBuilder buf = new StringBuilder ();
-
- buf.Append (visibility);
- buf.Append (" ");
-
- MemberFormatter full = new CSharpFullMemberFormatter ();
-
- if (DocUtils.IsDelegate (type)) {
- buf.Append("delegate ");
- MethodDefinition invoke = type.GetMethod ("Invoke");
- buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
- buf.Append (GetName (type));
- AppendParameters (buf, invoke, invoke.Parameters);
- AppendGenericTypeConstraints (buf, type);
- buf.Append (";");
-
- return buf.ToString();
- }
-
- if (type.IsAbstract && !type.IsInterface)
- buf.Append("abstract ");
- if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
- buf.Append("sealed ");
- buf.Replace ("abstract sealed", "static");
-
- buf.Append (GetTypeKind (type));
- buf.Append (" ");
- buf.Append (GetCSharpType (type.FullName) == null
- ? GetName (type)
- : type.Name);
-
- if (!type.IsEnum) {
- TypeReference basetype = type.BaseType;
- if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
- basetype = null;
-
- List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
- .Select (iface => full.GetName (iface))
- .OrderBy (s => s)
- .ToList ();
-
- if (basetype != null || interface_names.Count > 0)
- buf.Append (" : ");
-
- if (basetype != null) {
- buf.Append (full.GetName (basetype));
- if (interface_names.Count > 0)
- buf.Append (", ");
- }
-
- for (int i = 0; i < interface_names.Count; i++){
- if (i != 0)
- buf.Append (", ");
- buf.Append (interface_names [i]);
- }
- AppendGenericTypeConstraints (buf, type);
- }
-
- return buf.ToString ();
- }
-
- static string GetTypeKind (TypeDefinition t)
- {
- if (t.IsEnum)
- return "enum";
- if (t.IsValueType)
- return "struct";
- if (t.IsClass || t.FullName == "System.Enum")
- return "class";
- if (t.IsInterface)
- return "interface";
- throw new ArgumentException(t.FullName);
- }
-
- static string GetTypeVisibility (TypeAttributes ta)
- {
- switch (ta & TypeAttributes.VisibilityMask) {
- case TypeAttributes.Public:
- case TypeAttributes.NestedPublic:
- return "public";
-
- case TypeAttributes.NestedFamily:
- case TypeAttributes.NestedFamORAssem:
- return "protected";
-
- default:
- return null;
- }
- }
-
- protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
- {
- if (type.GenericParameters.Count == 0)
- return buf;
- return AppendConstraints (buf, type.GenericParameters);
- }
-
- private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
- {
- foreach (GenericParameter genArg in genArgs) {
- GenericParameterAttributes attrs = genArg.Attributes;
- IList<TypeReference> constraints = genArg.Constraints;
- if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
- continue;
-
- bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
- bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
- bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
- bool comma = false;
-
- if (!isref && !isvt && !isnew && constraints.Count == 0)
- continue;
- buf.Append (" where ").Append (genArg.Name).Append (" : ");
- if (isref) {
- buf.Append ("class");
- comma = true;
- }
- else if (isvt) {
- buf.Append ("struct");
- comma = true;
- }
- if (constraints.Count > 0 && !isvt) {
- if (comma)
- buf.Append (", ");
- buf.Append (GetTypeName (constraints [0]));
- for (int i = 1; i < constraints.Count; ++i)
- buf.Append (", ").Append (GetTypeName (constraints [i]));
- }
- if (isnew && !isvt) {
- if (comma)
- buf.Append (", ");
- buf.Append ("new()");
- }
- }
- return buf;
- }
-
- protected override string GetConstructorDeclaration (MethodDefinition constructor)
- {
- StringBuilder buf = new StringBuilder ();
- AppendVisibility (buf, constructor);
- if (buf.Length == 0)
- return null;
-
- buf.Append (' ');
- base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
- AppendParameters (buf, constructor, constructor.Parameters);
- buf.Append (';');
-
- return buf.ToString ();
- }
-
- protected override string GetMethodDeclaration (MethodDefinition method)
- {
- string decl = base.GetMethodDeclaration (method);
- if (decl != null)
- return decl + ";";
- return null;
- }
-
- protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
- {
- if (DocUtils.IsExplicitlyImplemented (method)) {
- TypeReference iface;
- MethodReference ifaceMethod;
- DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
- return buf.Append (new CSharpMemberFormatter ().GetName (iface))
- .Append ('.')
- .Append (ifaceMethod.Name);
- }
-
- if (method.Name.StartsWith ("op_", StringComparison.Ordinal)) {
- // this is an operator
- switch (method.Name) {
- case "op_Implicit":
- case "op_Explicit":
- buf.Length--; // remove the last space, which assumes a member name is coming
- return buf;
- case "op_Addition":
- case "op_UnaryPlus":
- return buf.Append ("operator +");
- case "op_Subtraction":
- case "op_UnaryNegation":
- return buf.Append ("operator -");
- case "op_Division":
- return buf.Append ("operator /");
- case "op_Multiply":
- return buf.Append ("operator *");
- case "op_Modulus":
- return buf.Append ("operator %");
- case "op_BitwiseAnd":
- return buf.Append ("operator &");
- case "op_BitwiseOr":
- return buf.Append ("operator |");
- case "op_ExclusiveOr":
- return buf.Append ("operator ^");
- case "op_LeftShift":
- return buf.Append ("operator <<");
- case "op_RightShift":
- return buf.Append ("operator >>");
- case "op_LogicalNot":
- return buf.Append ("operator !");
- case "op_OnesComplement":
- return buf.Append ("operator ~");
- case "op_Decrement":
- return buf.Append ("operator --");
- case "op_Increment":
- return buf.Append ("operator ++");
- case "op_True":
- return buf.Append ("operator true");
- case "op_False":
- return buf.Append ("operator false");
- case "op_Equality":
- return buf.Append ("operator ==");
- case "op_Inequality":
- return buf.Append ("operator !=");
- case "op_LessThan":
- return buf.Append ("operator <");
- case "op_LessThanOrEqual":
- return buf.Append ("operator <=");
- case "op_GreaterThan":
- return buf.Append ("operator >");
- case "op_GreaterThanOrEqual":
- return buf.Append ("operator >=");
- default:
- return base.AppendMethodName (buf, method);
- }
- }
- else
- return base.AppendMethodName (buf, method);
- }
-
- protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
- {
- if (method.GenericParameters.Count == 0)
- return buf;
- return AppendConstraints (buf, method.GenericParameters);
- }
-
- protected override string RefTypeModifier {
- get {return "";}
- }
-
- protected override string GetFinalizerName (MethodDefinition method)
- {
- return "~" + method.DeclaringType.Name + " ()";
- }
-
- protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
- {
- if (method == null)
- return buf;
- if (method.IsPublic)
- return buf.Append ("public");
- if (method.IsFamily || method.IsFamilyOrAssembly)
- return buf.Append ("protected");
- return buf;
- }
-
- protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
- {
- string modifiers = String.Empty;
- if (method.IsStatic) modifiers += " static";
- if (method.IsVirtual && !method.IsAbstract) {
- if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
- else modifiers += " override";
- }
- TypeDefinition declType = (TypeDefinition) method.DeclaringType;
- if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
- if (method.IsFinal) modifiers += " sealed";
- if (modifiers == " virtual sealed") modifiers = "";
-
- switch (method.Name) {
- case "op_Implicit":
- modifiers += " implicit operator";
- break;
- case "op_Explicit":
- modifiers += " explicit operator";
- break;
- }
-
- return buf.Append (modifiers);
- }
-
- protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
- {
- if (method.IsGenericMethod ()) {
- IList<GenericParameter> args = method.GenericParameters;
- if (args.Count > 0) {
- buf.Append ("<");
- buf.Append (args [0].Name);
- for (int i = 1; i < args.Count; ++i)
- buf.Append (",").Append (args [i].Name);
- buf.Append (">");
- }
- }
- return buf;
- }
-
- protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
- {
- return AppendParameters (buf, method, parameters, '(', ')');
- }
-
- private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
- {
- buf.Append (begin);
-
- if (parameters.Count > 0) {
- if (DocUtils.IsExtensionMethod (method))
- buf.Append ("this ");
- AppendParameter (buf, parameters [0]);
- for (int i = 1; i < parameters.Count; ++i) {
- buf.Append (", ");
- AppendParameter (buf, parameters [i]);
- }
- }
-
- return buf.Append (end);
- }
-
- private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
- {
- if (parameter.ParameterType is ByReferenceType) {
- if (parameter.IsOut)
- buf.Append ("out ");
- else
- buf.Append ("ref ");
- }
- if (parameter.HasCustomAttributes) {
- var isParams = parameter.CustomAttributes.Any (ca => ca.AttributeType.Name == "ParamArrayAttribute");
- if (isParams)
- buf.AppendFormat ("params ");
- }
- buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
- buf.Append (parameter.Name);
- if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant) {
- buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
- }
- return buf;
- }
-
- protected override string GetPropertyDeclaration (PropertyDefinition property)
- {
- MethodDefinition method;
-
- string get_visible = null;
- if ((method = property.GetMethod) != null &&
- (DocUtils.IsExplicitlyImplemented (method) ||
- (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
- get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
- string set_visible = null;
- if ((method = property.SetMethod) != null &&
- (DocUtils.IsExplicitlyImplemented (method) ||
- (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
- set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
-
- if ((set_visible == null) && (get_visible == null))
- return null;
-
- string visibility;
- StringBuilder buf = new StringBuilder ();
- if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
- buf.Append (visibility = get_visible);
- else if (set_visible != null && get_visible == null)
- buf.Append (visibility = set_visible);
- else
- buf.Append (visibility = "public");
-
- // Pick an accessor to use for static/virtual/override/etc. checks.
- method = property.SetMethod;
- if (method == null)
- method = property.GetMethod;
-
- string modifiers = String.Empty;
- if (method.IsStatic) modifiers += " static";
- if (method.IsVirtual && !method.IsAbstract) {
- if ((method.Attributes & MethodAttributes.NewSlot) != 0)
- modifiers += " virtual";
- else
- modifiers += " override";
- }
- TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
- if (method.IsAbstract && !declDef.IsInterface)
- modifiers += " abstract";
- if (method.IsFinal)
- modifiers += " sealed";
- if (modifiers == " virtual sealed")
- modifiers = "";
- buf.Append (modifiers).Append (' ');
-
- buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
-
- IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
- string name = property.Name;
- foreach (MemberReference mi in defs) {
- if (mi == property) {
- name = "this";
- break;
- }
- }
- buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
-
- if (property.Parameters.Count != 0) {
- AppendParameters (buf, method, property.Parameters, '[', ']');
- }
-
- buf.Append (" {");
- if (get_visible != null) {
- if (get_visible != visibility)
- buf.Append (' ').Append (get_visible);
- buf.Append (" get;");
- }
- if (set_visible != null) {
- if (set_visible != visibility)
- buf.Append (' ').Append (set_visible);
- buf.Append (" set;");
- }
- buf.Append (" }");
-
- return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
- }
-
- protected override string GetFieldDeclaration (FieldDefinition field)
- {
- TypeDefinition declType = (TypeDefinition) field.DeclaringType;
- if (declType.IsEnum && field.Name == "value__")
- return null; // This member of enums aren't documented.
-
- StringBuilder buf = new StringBuilder ();
- AppendFieldVisibility (buf, field);
- if (buf.Length == 0)
- return null;
-
- if (declType.IsEnum)
- return field.Name;
-
- if (field.IsStatic && !field.IsLiteral)
- buf.Append (" static");
- if (field.IsInitOnly)
- buf.Append (" readonly");
- if (field.IsLiteral)
- buf.Append (" const");
-
- buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
- buf.Append (field.Name);
- AppendFieldValue (buf, field);
- buf.Append (';');
-
- return buf.ToString ();
- }
-
- static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
- {
- if (field.IsPublic)
- return buf.Append ("public");
- if (field.IsFamily || field.IsFamilyOrAssembly)
- return buf.Append ("protected");
- return buf;
- }
-
- static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
- {
- // enums have a value__ field, which we ignore
- if (((TypeDefinition ) field.DeclaringType).IsEnum ||
- field.DeclaringType.IsGenericType ())
- return buf;
- if (field.HasConstant && field.IsLiteral) {
- object val = null;
- try {
- val = field.Constant;
- } catch {
- return buf;
- }
- if (val == null)
- buf.Append (" = ").Append ("null");
- else if (val is Enum)
- buf.Append (" = ").Append (val.ToString ());
- else if (val is IFormattable) {
- string value = ((IFormattable)val).ToString(null, CultureInfo.InvariantCulture);
- if (val is string)
- value = "\"" + value + "\"";
- buf.Append (" = ").Append (value);
- }
- }
- return buf;
- }
-
- protected override string GetEventDeclaration (EventDefinition e)
- {
- StringBuilder buf = new StringBuilder ();
- if (AppendVisibility (buf, e.AddMethod).Length == 0) {
- return null;
- }
-
- AppendModifiers (buf, e.AddMethod);
-
- buf.Append (" event ");
- buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters [0]))).Append (' ');
- buf.Append (e.Name).Append (';');
-
- return buf.ToString ();
- }
-}
-
-public class CSharpMemberFormatter : CSharpFullMemberFormatter {
- protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
- {
- return buf;
- }
-}
-
-class DocTypeFullMemberFormatter : MemberFormatter {
- public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
-
- protected override char NestedTypeSeparator {
- get {return '+';}
- }
-}
-
-class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
- protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
- {
- return buf;
- }
-}
-
-class SlashDocMemberFormatter : MemberFormatter {
-
- protected override char[] GenericTypeContainer {
- get {return new char[]{'{', '}'};}
- }
-
- private bool AddTypeCount = true;
-
- private TypeReference genDeclType;
- private MethodReference genDeclMethod;
-
- protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
- {
- if (type is GenericParameter) {
- int l = buf.Length;
- if (genDeclType != null) {
- IList<GenericParameter> genArgs = genDeclType.GenericParameters;
- for (int i = 0; i < genArgs.Count; ++i) {
- if (genArgs [i].Name == type.Name) {
- buf.Append ('`').Append (i);
- break;
- }
- }
- }
- if (genDeclMethod != null) {
- IList<GenericParameter> genArgs = null;
- if (genDeclMethod.IsGenericMethod ()) {
- genArgs = genDeclMethod.GenericParameters;
- for (int i = 0; i < genArgs.Count; ++i) {
- if (genArgs [i].Name == type.Name) {
- buf.Append ("``").Append (i);
- break;
- }
- }
- }
- }
- if (genDeclType == null && genDeclMethod == null) {
- // Probably from within an explicitly implemented interface member,
- // where CSC uses parameter names instead of indices (why?), e.g.
- // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
- // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
- buf.Append (type.Name);
- }
- if (buf.Length == l) {
- throw new Exception (string.Format (
- "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
- type.Name, genDeclType, genDeclMethod));
- }
- }
- else {
- base.AppendTypeName (buf, type, context);
- if (AddTypeCount) {
- int numArgs = type.GenericParameters.Count;
- if (type.DeclaringType != null)
- numArgs -= type.GenericParameters.Count;
- if (numArgs > 0) {
- buf.Append ('`').Append (numArgs);
- }
- }
- }
- return buf;
- }
-
- protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
- {
- buf.Append (ArrayDelimeters [0]);
- int rank = array.Rank;
- if (rank > 1) {
- buf.Append ("0:");
- for (int i = 1; i < rank; ++i) {
- buf.Append (",0:");
- }
- }
- return buf.Append (ArrayDelimeters [1]);
- }
-
- protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
- {
- if (!AddTypeCount)
- base.AppendGenericType (buf, type, context);
- else
- AppendType (buf, type, context);
- return buf;
- }
-
- private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
- {
- List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
- bool insertNested = false;
- int prevParamCount = 0;
- foreach (var decl in decls) {
- if (insertNested)
- buf.Append (NestedTypeSeparator);
- insertNested = true;
- base.AppendTypeName (buf, decl, context);
- int argCount = DocUtils.GetGenericArgumentCount (decl);
- int numArgs = argCount - prevParamCount;
- prevParamCount = argCount;
- if (numArgs > 0)
- buf.Append ('`').Append (numArgs);
- }
- return buf;
- }
-
- protected override string GetConstructorName (MethodReference constructor)
- {
- return GetMethodDefinitionName (constructor, "#ctor");
- }
-
- protected override string GetMethodName (MethodReference method)
- {
- string name = null;
- MethodDefinition methodDef = method as MethodDefinition;
- if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
- name = method.Name;
- else {
- TypeReference iface;
- MethodReference ifaceMethod;
- DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
- AddTypeCount = false;
- name = GetTypeName (iface) + "." + ifaceMethod.Name;
- AddTypeCount = true;
- }
- return GetMethodDefinitionName (method, name);
- }
-
- private string GetMethodDefinitionName (MethodReference method, string name)
- {
- StringBuilder buf = new StringBuilder ();
- buf.Append (GetTypeName (method.DeclaringType));
- buf.Append ('.');
- buf.Append (name.Replace (".", "#"));
- if (method.IsGenericMethod ()) {
- IList<GenericParameter> genArgs = method.GenericParameters;
- if (genArgs.Count > 0)
- buf.Append ("``").Append (genArgs.Count);
- }
- IList<ParameterDefinition> parameters = method.Parameters;
- try {
- genDeclType = method.DeclaringType;
- genDeclMethod = method;
- AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
- }
- finally {
- genDeclType = null;
- genDeclMethod = null;
- }
- return buf.ToString ();
- }
-
- private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
- {
- if (parameters.Count == 0)
- return buf;
-
- buf.Append ('(');
-
- AppendParameter (buf, genArgs, parameters [0]);
- for (int i = 1; i < parameters.Count; ++i) {
- buf.Append (',');
- AppendParameter (buf, genArgs, parameters [i]);
- }
-
- return buf.Append (')');
- }
-
- private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
- {
- AddTypeCount = false;
- buf.Append (GetTypeName (parameter.ParameterType));
- AddTypeCount = true;
- return buf;
- }
-
- protected override string GetPropertyName (PropertyReference property)
- {
- string name = null;
-
- PropertyDefinition propertyDef = property as PropertyDefinition;
- MethodDefinition method = null;
- if (propertyDef != null)
- method = propertyDef.GetMethod ?? propertyDef.SetMethod;
- if (method != null && !DocUtils.IsExplicitlyImplemented (method))
- name = property.Name;
- else {
- TypeReference iface;
- MethodReference ifaceMethod;
- DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
- AddTypeCount = false;
- name = string.Join ("#", new string[]{
- GetTypeName (iface).Replace (".", "#"),
- DocUtils.GetMember (property.Name)
- });
- AddTypeCount = true;
- }
-
- StringBuilder buf = new StringBuilder ();
- buf.Append (GetName (property.DeclaringType));
- buf.Append ('.');
- buf.Append (name);
- IList<ParameterDefinition> parameters = property.Parameters;
- if (parameters.Count > 0) {
- genDeclType = property.DeclaringType;
- buf.Append ('(');
- IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
- AppendParameter (buf, genArgs, parameters [0]);
- for (int i = 1; i < parameters.Count; ++i) {
- buf.Append (',');
- AppendParameter (buf, genArgs, parameters [i]);
- }
- buf.Append (')');
- genDeclType = null;
- }
- return buf.ToString ();
- }
-
- protected override string GetFieldName (FieldReference field)
- {
- return string.Format ("{0}.{1}",
- GetName (field.DeclaringType), field.Name);
- }
-
- protected override string GetEventName (EventReference e)
- {
- return string.Format ("{0}.{1}",
- GetName (e.DeclaringType), e.Name);
- }
-
- protected override string GetTypeDeclaration (TypeDefinition type)
- {
- string name = GetName (type);
- if (type == null)
- return null;
- return "T:" + name;
- }
-
- protected override string GetConstructorDeclaration (MethodDefinition constructor)
- {
- string name = GetName (constructor);
- if (name == null)
- return null;
- return "M:" + name;
- }
-
- protected override string GetMethodDeclaration (MethodDefinition method)
- {
- string name = GetName (method);
- if (name == null)
- return null;
- if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
- genDeclType = method.DeclaringType;
- genDeclMethod = method;
- name += "~" + GetName (method.ReturnType);
- genDeclType = null;
- genDeclMethod = null;
- }
- return "M:" + name;
- }
-
- protected override string GetPropertyDeclaration (PropertyDefinition property)
- {
- string name = GetName (property);
- if (name == null)
- return null;
- return "P:" + name;
- }
-
- protected override string GetFieldDeclaration (FieldDefinition field)
- {
- string name = GetName (field);
- if (name == null)
- return null;
- return "F:" + name;
- }
-
- protected override string GetEventDeclaration (EventDefinition e)
- {
- string name = GetName (e);
- if (name == null)
- return null;
- return "E:" + name;
- }
-}
-
-class FileNameMemberFormatter : SlashDocMemberFormatter {
- protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
- {
- return buf;
- }
-
- protected override char NestedTypeSeparator {
- get {return '+';}
- }
-}
-
-class ResolvedTypeInfo {
- TypeDefinition typeDef;
-
- public ResolvedTypeInfo (TypeReference value) {
- Reference = value;
- }
-
- public TypeReference Reference { get; private set; }
-
- public TypeDefinition Definition {
- get {
- if (typeDef == null) {
- typeDef = Reference.Resolve ();
- }
- return typeDef;
- }
- }
-}
-
-/// <summary>Formats attribute values. Should return true if it is able to format the value.</summary>
-class AttributeValueFormatter {
- public virtual bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
- {
- TypeReference valueType = type.Reference;
- if (v == null) {
- returnvalue = "null";
- return true;
- }
- if (valueType.FullName == "System.Type") {
- var vTypeRef = v as TypeReference;
- if (vTypeRef != null)
- returnvalue = "typeof(" + NativeTypeManager.GetTranslatedName (vTypeRef) + ")"; // TODO: drop NS handling
- else
- returnvalue = "typeof(" + v.ToString () + ")";
-
- return true;
- }
- if (valueType.FullName == "System.String") {
- returnvalue = "\"" + v.ToString () + "\"";
- return true;
- }
- if (valueType.FullName == "System.Char") {
- returnvalue = "'" + v.ToString () + "'";
- return true;
- }
- if (v is Boolean) {
- returnvalue = (bool)v ? "true" : "false";
- return true;
- }
-
- TypeDefinition valueDef = type.Definition;
- if (valueDef == null || !valueDef.IsEnum) {
- returnvalue = v.ToString ();
- return true;
- }
-
- string typename = MDocUpdater.GetDocTypeFullName (valueType);
- var values = MDocUpdater.GetEnumerationValues (valueDef);
- long c = MDocUpdater.ToInt64 (v);
- if (values.ContainsKey (c)) {
- returnvalue = typename + "." + values [c];
- return true;
- }
-
- returnvalue = null;
- return false;
- }
-}
-
-/// <summary>The final value formatter in the pipeline ... if no other formatter formats the value,
-/// then this one will serve as the default implementation.</summary>
-class DefaultAttributeValueFormatter : AttributeValueFormatter {
- public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
- {
- returnvalue = "(" + MDocUpdater.GetDocTypeFullName (type.Reference) + ") " + v.ToString ();
- return true;
- }
-}
-
-/// <summary>Flags enum formatter that assumes powers of two values.</summary>
-/// <remarks>As described here: https://msdn.microsoft.com/en-us/library/vstudio/ms229062(v=vs.100).aspx</remarks>
-class StandardFlagsEnumFormatter : AttributeValueFormatter {
- public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
- {
- TypeReference valueType = type.Reference;
- TypeDefinition valueDef = type.Definition;
- if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
-
- string typename = MDocUpdater.GetDocTypeFullName (valueType);
- var values = MDocUpdater.GetEnumerationValues (valueDef);
- long c = MDocUpdater.ToInt64 (v);
- returnvalue = string.Join (" | ",
- (from i in values.Keys
- where (c & i) == i && i != 0
- select typename + "." + values [i])
- .DefaultIfEmpty (c.ToString ()).ToArray ());
-
- return true;
- }
-
- returnvalue = null;
- return false;
- }
-}
-
-/// <summary>A custom formatter for the ObjCRuntime.Platform enumeration.</summary>
-class ApplePlatformEnumFormatter : AttributeValueFormatter {
- public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
- {
- TypeReference valueType = type.Reference;
- string typename = MDocUpdater.GetDocTypeFullName (valueType);
- TypeDefinition valueDef = type.Definition;
- if (typename.Contains ("ObjCRuntime.Platform") && valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
-
- var values = MDocUpdater.GetEnumerationValues (valueDef);
- long c = MDocUpdater.ToInt64 (v);
-
- returnvalue = Format (c, values, typename);
- return true;
- }
-
- returnvalue = null;
- return false;
- }
-
- string Format (long c, IDictionary<long, string> values, string typename)
- {
- int iosarch, iosmajor, iosminor, iossubminor;
- int macarch, macmajor, macminor, macsubminor;
- GetEncodingiOS (c, out iosarch, out iosmajor, out iosminor, out iossubminor);
- GetEncodingMac ((ulong)c, out macarch, out macmajor, out macminor, out macsubminor);
-
- if (iosmajor == 0 & iosminor == 0 && iossubminor == 0) {
- return FormatValues ("Mac", macarch, macmajor, macminor, macsubminor);
- }
-
- if (macmajor == 0 & macminor == 0 && macsubminor == 0) {
- return FormatValues ("iOS", iosarch, iosmajor, iosminor, iossubminor);
- }
-
- return string.Format ("(Platform){0}", c);
- }
-
- string FormatValues (string plat, int arch, int major, int minor, int subminor)
- {
- string archstring = "";
- switch (arch) {
- case 1:
- archstring = "32";
- break;
- case 2:
- archstring = "64";
- break;
- }
- return string.Format ("Platform.{4}_{0}_{1}{2} | Platform.{4}_Arch{3}",
- major,
- minor,
- subminor == 0 ? "" : "_" + subminor.ToString (),
- archstring,
- plat
- );
- }
-
- void GetEncodingiOS (long entireLong, out int archindex, out int major, out int minor, out int subminor)
- {
- long lowerBits = entireLong & 0xffffffff;
- int lowerBitsAsInt = (int) lowerBits;
- GetEncoding (lowerBitsAsInt, out archindex, out major, out minor, out subminor);
- }
-
- void GetEncodingMac (ulong entireLong, out int archindex, out int major, out int minor, out int subminor)
- {
- ulong higherBits = entireLong & 0xffffffff00000000;
- int higherBitsAsInt = (int) ((higherBits) >> 32);
- GetEncoding (higherBitsAsInt, out archindex, out major, out minor, out subminor);
- }
-
- void GetEncoding (Int32 encodedBits, out int archindex, out int major, out int minor, out int subminor)
- {
- // format is AAJJNNSS
- archindex = (int)((encodedBits & 0xFF000000) >> 24);
- major = (int)((encodedBits & 0x00FF0000) >> 16);
- minor = (int)((encodedBits & 0x0000FF00) >> 8);
- subminor = (int)((encodedBits & 0x000000FF) >> 0);
- }
-}
-} \ No newline at end of file