diff options
41 files changed, 7301 insertions, 6463 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 diff --git a/mdoc/mdoc.Test/FormatterTests.cs b/mdoc/mdoc.Test/FormatterTests.cs index 799ad138..30a98133 100644 --- a/mdoc/mdoc.Test/FormatterTests.cs +++ b/mdoc/mdoc.Test/FormatterTests.cs @@ -7,6 +7,8 @@ using Mono.Cecil; using mdoc.Test.SampleClasses; using System.Linq.Expressions; +using Mono.Documentation.Updater; + namespace mdoc.Test { [TestFixture ()] diff --git a/mdoc/mdoc.csproj b/mdoc/mdoc.csproj index ce716613..8bc16065 100644 --- a/mdoc/mdoc.csproj +++ b/mdoc/mdoc.csproj @@ -51,6 +51,36 @@ </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
+ <Compile Include="Mono.Documentation\Updater\Formatters\ApplePlatformEnumFormatter.cs" />
+ <Compile Include="Mono.Documentation\Updater\Formatters\StandardFlagsEnumFormatter.cs" />
+ <Compile Include="Mono.Documentation\Updater\Formatters\DefaultAttributeValueFormatter.cs" />
+ <Compile Include="Mono.Documentation\Updater\Formatters\AttributeValueFormatter.cs" />
+ <Compile Include="Mono.Documentation\Updater\ResolvedTypeInfo.cs" />
+ <Compile Include="Mono.Documentation\Updater\Formatters\FileNameMemberFormatter.cs" />
+ <Compile Include="Mono.Documentation\Updater\Formatters\SlashDocMemberFormatter.cs" />
+ <Compile Include="Mono.Documentation\Updater\Formatters\DocTypeMemberFormatter.cs" />
+ <Compile Include="Mono.Documentation\Updater\Formatters\DocTypeFullMemberFormatter.cs" />
+ <Compile Include="Mono.Documentation\Updater\Formatters\CSharpMemberFormatter.cs" />
+ <Compile Include="Mono.Documentation\Updater\Formatters\CSharpFullMemberFormatter.cs" />
+ <Compile Include="Mono.Documentation\Updater\Formatters\CSharpNativeTypeMemberFormatter.cs" />
+ <Compile Include="Mono.Documentation\Updater\Formatters\ILNativeTypeMemberFormatter.cs" />
+ <Compile Include="Mono.Documentation\Updater\Formatters\ILMemberFormatter.cs" />
+ <Compile Include="Mono.Documentation\Updater\Formatters\ILFullMemberFormatter.cs" />
+ <Compile Include="Mono.Documentation\Updater\Formatters\DocIdFormatter.cs" />
+ <Compile Include="Mono.Documentation\Updater\Formatters\MemberFormatter.cs" />
+ <Compile Include="Mono.Documentation\Updater\MemberFormatterState.cs" />
+ <Compile Include="Mono.Documentation\Updater\DynamicParserContext.cs" />
+ <Compile Include="Mono.Documentation\Updater\DocumentationMember.cs" />
+ <Compile Include="Mono.Documentation\Updater\EcmaDocumentationImporter.cs" />
+ <Compile Include="Mono.Documentation\Updater\MsxdocDocumentationImporter.cs" />
+ <Compile Include="Mono.Documentation\Updater\DocumentationImporter.cs" />
+ <Compile Include="Mono.Documentation\Updater\EcmaDocumentationEnumerator.cs" />
+ <Compile Include="Mono.Documentation\Updater\DocumentationEnumerator.cs" />
+ <Compile Include="Mono.Documentation\Updater\DocsNodeInfo.cs" />
+ <Compile Include="Mono.Documentation\Util\ApiStyle.cs" />
+ <Compile Include="Mono.Documentation\Updater\DocUtils.cs" />
+ <Compile Include="Mono.Documentation\Util\CecilExtensions.cs" />
+ <Compile Include="Mono.Documentation\Util\NativeTypeManager.cs" />
<Compile Include="Mono.Rocks\ObjectRocks.cs" />
<Compile Include="Mono.Rocks\StreamRocks.cs" />
<Compile Include="Mono.Documentation\assembler.cs" />
@@ -58,7 +88,6 @@ <Compile Include="Mono.Documentation\ecmadoc.cs" />
<Compile Include="Mono.Documentation\mdoc.cs" />
<Compile Include="Mono.Documentation\MdocFile.cs" />
- <Compile Include="Mono.Documentation\monodocer.cs" />
<Compile Include="Mono.Documentation\monodocs2html.cs" />
<Compile Include="Mono.Documentation\monodocs2slashdoc.cs" />
<Compile Include="Mono.Documentation\msitomsx.cs" />
@@ -71,11 +100,12 @@ <Compile Include="Mono.Documentation\frameworksbootstrapper.cs" />
<Compile Include="Consts.cs" />
<Compile Include="Options.cs" />
- <Compile Include="Mono.Documentation\Frameworks\FrameworkIndex.cs" />
- <Compile Include="Mono.Documentation\Frameworks\FrameworkEntry.cs" />
- <Compile Include="Mono.Documentation\Frameworks\FrameworkTypeEntry.cs" />
- <Compile Include="Mono.Documentation\Frameworks\AssemblySet.cs" />
- <Compile Include="Mono.Documentation\Frameworks\UwpResolver.cs" />
+ <Compile Include="Mono.Documentation\Updater\Frameworks\FrameworkIndex.cs" />
+ <Compile Include="Mono.Documentation\Updater\Frameworks\FrameworkEntry.cs" />
+ <Compile Include="Mono.Documentation\Updater\Frameworks\FrameworkTypeEntry.cs" />
+ <Compile Include="Mono.Documentation\Updater\Frameworks\AssemblySet.cs" />
+ <Compile Include="Mono.Documentation\Updater\Frameworks\UwpResolver.cs" />
+ <Compile Include="Mono.Documentation\MDocUpdater.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\defaulttemplate.xsl">
|