/mcs/tools/mdoc/Mono.Documentation/monodocer.cs
C# | 5008 lines | 4465 code | 460 blank | 83 comment | 1000 complexity | 30cc9fee6844ac8e85177f7b58d515c2 MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
Large files files are truncated, but you can click here to view the full file
- // 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.XPath;
- using Mono.Cecil;
- 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 {
- class MDocUpdater : MDocCommand
- {
- string srcPath;
- List<AssemblyDefinition> assemblies;
- readonly DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver();
-
- 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 ();
- HashSet<string> forwardedTypes = new HashSet<string> ();
- public override void Run (IEnumerable<string> args)
- {
- 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 => assemblyResolver.AddSearchDirectory (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 => assemblyResolver.AddSearchDirectory (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) },
- };
- var assemblies = Parse (p, args, "update",
- "[OPTIONS]+ ASSEMBLIES",
- "Create or update documentation from ASSEMBLIES.");
- if (assemblies == null)
- return;
- if (assemblies.Count == 0)
- Error ("No assemblies specified.");
- foreach (var dir in assemblies
- .Where (a => a.Contains (Path.DirectorySeparatorChar))
- .Select (a => Path.GetDirectoryName (a)))
- assemblyResolver.AddSearchDirectory (dir);
- // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
-
- if (srcPath == null)
- throw new InvalidOperationException("The --out option is required.");
-
- this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
- // Store types that have been forwarded to avoid duplicate generation
- GatherForwardedTypes ();
- docEnum = docEnum ?? new DocumentationEnumerator ();
-
- // PERFORM THE UPDATES
-
- if (types.Count > 0) {
- types.Sort ();
- DoUpdateTypes (srcPath, types, srcPath);
- }
- #if false
- else if (opts.@namespace != null)
- DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
- Path.Combine (dest_dir, opts.@namespace));
- #endif
- else
- DoUpdateAssemblies (srcPath, srcPath);
- Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
- }
- void AddImporter (string path)
- {
- 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") {
- importers.Add (new MsxdocDocumentationImporter (path));
- }
- else if (r.LocalName == "Libraries") {
- var ecmadocs = new XmlTextReader (path);
- docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
- importers.Add (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);
- }
- }
- void GatherForwardedTypes ()
- {
- foreach (var asm in assemblies)
- foreach (var type in asm.MainModule.ExportedTypes.Where (t => t.IsForwarder).Select (t => t.FullName))
- forwardedTypes.Add (type);
- }
- 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);
- }
-
- private AssemblyDefinition LoadAssembly (string name)
- {
- AssemblyDefinition assembly = null;
- try {
- assembly = AssemblyDefinition.ReadAssembly (name, new ReaderParameters { AssemblyResolver = assemblyResolver });
- } catch (System.IO.FileNotFoundException) { }
- if (assembly == null)
- throw new InvalidOperationException("Assembly " + name + " not found.");
- 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;
- c.Remove (n);
- 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 (AssemblyDefinition assembly in assemblies) {
- foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames)) {
- string relpath = DoUpdateType (type, 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 ());
- }
- public string DoUpdateType (TypeDefinition type, 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 reltypefile = DocUtils.PathCombine (DocUtils.GetNamespace (type), typename + ".xml");
- string typefile = Path.Combine (basepath, reltypefile);
- System.IO.FileInfo file = new System.IO.FileInfo(typefile);
- string output = null;
- if (dest == null) {
- output = typefile;
- } else if (dest == "-") {
- output = null;
- } else {
- output = Path.Combine (dest, reltypefile);
- }
- if (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, output, false);
- } else {
- // Stub
- XmlElement td = StubType(type, output);
- if (td == null)
- return null;
-
- System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
- if (!dir.Exists) {
- dir.Create();
- Console.WriteLine("Namespace Directory Created: " + type.Namespace);
- }
- }
- return reltypefile;
- }
- public void DoUpdateNS (string ns, string nspath, string outpath)
- {
- Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
- AssemblyDefinition assembly = assemblies [0];
- foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
- XmlDocument basefile = new XmlDocument();
- string typefile = Path.Combine(nspath, file.Name);
- try {
- basefile.Load(typefile);
- } catch (Exception e) {
- throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
- }
- string typename =
- GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
- TypeDefinition type = assembly.GetType(typename);
- if (type == null) {
- Warning ("Type no longer in assembly: " + typename);
- continue;
- }
- seenTypes[type] = seenTypes;
- DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false);
- }
-
- // Stub types not in the directory
- foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
- if (type.Namespace != ns || seenTypes.ContainsKey(type))
- continue;
- XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"));
- if (td == null) continue;
- }
- }
-
- 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 = 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();
- }
-
- string defaultTitle = "Untitled";
- if (assemblies.Count == 1)
- defaultTitle = assemblies[0].Name.Name;
- WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
-
- XmlElement index_types = WriteElement(index.DocumentElement, "Types");
- XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
- index_assemblies.RemoveAll ();
- HashSet<string> goodfiles = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
- foreach (AssemblyDefinition assm in assemblies) {
- AddIndexAssembly (assm, index_assemblies);
- DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
- }
- 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 (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
- {
- foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
- string typename = GetTypeFileName(type);
- if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0 || forwardedTypes.Contains (type.FullName))
- continue;
- string reltypepath = DoUpdateType (type, 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 onsdoc = DocUtils.PathCombine (dest, type.Namespace + ".xml");
- string nsdoc = DocUtils.PathCombine (dest, "ns-" + type.Namespace + ".xml");
- if (File.Exists (onsdoc)) {
- File.Move (onsdoc, nsdoc);
- }
- if (!File.Exists (nsdoc)) {
- Console.WriteLine("New Namespace File: " + type.Namespace);
- WriteNamespaceStub(type.Namespace, 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);
- }
- private 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("*")) {
- 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;
- if (e != null && !no_assembly_versions && UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
- using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
- WriteXml(doc.DocumentElement, writer);
- goodfiles.Add (relTypeFile);
- continue;
- }
- string newname = typefile.FullName + ".remove";
- try { System.IO.File.Delete(newname); } catch (Exception) { }
- try { typefile.MoveTo(newname); } catch (Exception) { }
- Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
- }
- }
- }
- }
- 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 ()
- {
- return (from a in assemblies 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, string output, bool insertSince)
- {
- Console.WriteLine(message + ": " + type.FullName);
-
- StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
- // Update type metadata
- UpdateType(basefile.DocumentElement, type);
- // 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.
- if (true) {
- MyXmlNodeList todelete = new MyXmlNodeList ();
- foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
- XmlElement oldmember = info.Node;
- MemberReference oldmember2 = info.Member;
- string sig = oldmember2 != null ? memberFormatters [0].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 (!no_assembly_versions && UpdateAssemblyVersions (oldmember, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
- continue;
- DeleteMember ("Member Removed", output, oldmember, todelete);
- continue;
- }
-
- // Duplicated
- if (seenmembers.ContainsKey (sig)) {
- if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
- // ignore, already seen
- }
- else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
- DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
- else
- Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
- continue;
- }
-
- // Update signature information
- UpdateMember(info);
-
- seenmembers.Add (sig, oldmember);
- }
- foreach (XmlElement oldmember in todelete)
- oldmember.ParentNode.RemoveChild (oldmember);
- }
-
- if (!DocUtils.IsDelegate (type)) {
- XmlNode members = WriteElement (basefile.DocumentElement, "Members");
- foreach (MemberReference m in type.GetMembers()) {
- if (m is TypeDefinition) continue;
-
- string sig = memberFormatters [0].GetDeclaration (m);
- if (sig == null) continue;
- if (seenmembers.ContainsKey(sig)) continue;
-
- XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
- if (mm == null) continue;
- members.AppendChild( mm );
-
- 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)
- {
- string format = output != null
- ? "{0}: File='{1}'; Signature='{4}'"
- : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
- Warning (format,
- reason,
- output,
- member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
- member.Attributes ["MemberName"].Value,
- member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
- if (!delete && MemberDocsHaveUserContent (member)) {
- Warning ("Member deletions must be enabled with the --delete option.");
- } else {
- todelete.Add (member);
- deletions++;
- }
- }
- 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;
- // 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"), 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)
- {
- 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);
- DoUpdateType2 ("New Type", doc, type, 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)
- {
- root.SetAttribute("Name", GetDocTypeName (type));
- root.SetAttribute("FullName", GetDocTypeFullName (type));
- foreach (MemberFormatter f in typeFormatters) {
- string element = "TypeSignature[@Language='" + f.Language + "']";
- WriteElementAttribute (root, element, "Language", f.Language);
- WriteElementAttribute (root, element, "Value", f.GetDeclaration (type));
- }
-
- XmlElement ass = WriteElement(root, "AssemblyInfo");
- WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
- if (!no_assembly_versions) {
- UpdateAssemblyVersions (root, 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);
- } 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";
- WriteElementText(root, "Base/BaseTypeName", basetypename);
-
- // 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));
-
- if (DocUtils.IsDelegate (type)) {
- MakeTypeParameters (root, type.GenericParameters);
- MakeParameters(root, type.GetMethod("Invoke").Parameters);
- MakeReturnValue(root, type.GetMethod("Invoke"));
- }
-
- DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
- MakeDocNode (typeInfo);
-
- if (!DocUtils.IsDelegate (type))
- WriteElement (root, "Members");
- OrderTypeNodes (root, root.ChildNodes);
- NormalizeWhitespace(root);
- }
- 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)
- {
- XmlElement me = (XmlElement) info.Node;
- MemberReference mi = info.Member;
- foreach (MemberFormatter f in memberFormatters) {
- string element = "MemberSignature[@Language='" + f.Language + "']";
- WriteElementAttribute (me, element, "Language", f.Language);
- WriteElementAttribute (me, element, "Value", f.GetDeclaration (mi));
- }
- WriteElementText(me, "MemberType", GetMemberType(mi));
-
- if (!no_assembly_versions) {
- UpdateAssemblyVersions (me, mi, true);
- }
- else {
- ClearElement (me, "AssemblyInfo");
- }
- MakeAttributes (me, GetCustomAttributes (mi));
- MakeReturnValue(me, mi);
- if (mi is MethodReference) {
- MethodReference mb = (MethodReference) mi;
- if (mb.IsGenericMethod ())
- MakeTypeParameters (me, mb.GenericParameters);
- }
- MakeParameters(me, mi);
-
- string fieldValue;
- if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
- WriteElementText(me, "MemberValue", fieldValue);
-
- info.Node = WriteElement (me, "Docs");
- MakeDocNode (info);
- OrderMemberNodes (me, me.ChildNodes);
- UpdateExtensionMethods (me, info);
- }
- 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");
- link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
- link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
- member.AppendChild (link);
- AddTargets (em, info);
- 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)) {
- AppendElementAttributeText (targets, "Target", "Type",
- slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
- }
- 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();
- 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) {
- XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
- if (ret == null) {
- string[] path = element.Split('/');
- foreach (string p in path) {
- ret = (XmlElement)parent.SelectSingleNode(p);
- if (ret == null) {
- 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 void WriteElementText(XmlNode parent, string element, string value) {
- XmlElement node = WriteElement(parent, element);
- node.InnerText = value;
- }
- 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;
- }
- pr…
Large files files are truncated, but you can click here to view the full file