1 // Updater program for syncing Mono's ECMA-style documentation files
3 // By Joshua Tauberer <tauberer@for.net>
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Collections.ObjectModel;
9 using System.Diagnostics;
10 using System.Globalization;
15 using System.Xml.XPath;
20 using MyXmlNodeList = System.Collections.Generic.List<System.Xml.XmlNode>;
21 using StringList = System.Collections.Generic.List<string>;
22 using StringToStringMap = System.Collections.Generic.Dictionary<string, string>;
23 using StringToXmlNodeMap = System.Collections.Generic.Dictionary<string, System.Xml.XmlNode>;
25 namespace Mono.Documentation {
26 static class NativeTypeManager {
28 static Dictionary<string, string> toNativeType = new Dictionary<string,string>(){
32 {"System.Int32", "System.nint"},
35 {"System.UInt32", "System.nuint"},
38 {"System.Single", "System.nfloat"},
39 {"SizeF", "CoreGraphics.CGSize"},
40 {"System.Drawing.SizeF", "CoreGraphics.CGSize"},
41 {"PointF", "CoreGraphics.CGPoint"},
42 {"System.Drawing.PointF", "CoreGraphics.CGPoint"},
43 {"RectangleF", "CoreGraphics.CGRect" },
44 {"System.Drawing.RectangleF", "CoreGraphics.CGRect"}
47 static Dictionary<string, string> fromNativeType = new Dictionary<string,string>(){
50 {"System.nint", "System.Int32"},
52 {"System.nuint", "System.UInt32"},
54 {"System.nfloat", "System.Single"},
55 {"CoreGraphics.CGSize", "System.Drawing.SizeF"},
56 {"CoreGraphics.CGPoint", "System.Drawing.PointF"},
57 {"CoreGraphics.CGRect", "System.Drawing.RectangleF"},
58 {"MonoTouch.CoreGraphics.CGSize", "System.Drawing.SizeF"},
59 {"MonoTouch.CoreGraphics.CGPoint", "System.Drawing.PointF"},
60 {"MonoTouch.CoreGraphics.CGRect", "System.Drawing.RectangleF"}
63 public static string ConvertToNativeType(string typename) {
68 string valueToCompare = StripToComparableType (typename, ref isOut, ref isArray);
70 if (toNativeType.TryGetValue (valueToCompare, out nvalue)) {
82 public static string ConvertFromNativeType(string typename) {
87 string valueToCompare = StripToComparableType (typename, ref isOut, ref isArray);
89 if (fromNativeType.TryGetValue (valueToCompare, out nvalue)) {
98 // it wasn't one of the native types ... just return it
102 static string StripToComparableType (string typename, ref bool isOut, ref bool isArray)
104 string valueToCompare = typename;
105 if (typename.EndsWith ("[]")) {
106 valueToCompare = typename.Substring (0, typename.Length - 2);
109 if (typename.EndsWith ("&")) {
110 valueToCompare = typename.Substring (0, typename.Length - 1);
113 if (typename.Contains ("<")) {
114 // TODO: Need to recursively process generic parameters
116 return valueToCompare;
119 public static string GetTranslatedName(TypeReference t) {
120 string typename = t.FullName;
122 bool isInAssembly = MDocUpdater.IsInAssemblies (t.Module.Name);
123 if (isInAssembly && !typename.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (t)) {
124 string nameWithDropped = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typename);
125 return nameWithDropped;
130 class MDocUpdater : MDocCommand
133 List<AssemblyDefinition> assemblies;
134 readonly DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver();
138 bool show_exceptions;
139 bool no_assembly_versions, ignore_missing_types;
140 ExceptionLocations? exceptions;
142 internal int additions = 0, deletions = 0;
144 List<DocumentationImporter> importers = new List<DocumentationImporter> ();
146 DocumentationEnumerator docEnum;
150 static readonly MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
151 static readonly MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
153 static MemberFormatter[] typeFormatters = new MemberFormatter[]{
154 new CSharpMemberFormatter (),
155 new ILMemberFormatter (),
158 static MemberFormatter[] memberFormatters = new MemberFormatter[]{
159 new CSharpFullMemberFormatter (),
160 new ILFullMemberFormatter (),
163 internal static readonly MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
165 MyXmlNodeList extensionMethods = new MyXmlNodeList ();
167 HashSet<string> forwardedTypes = new HashSet<string> ();
169 public static string droppedNamespace = string.Empty;
171 public static bool HasDroppedNamespace(TypeDefinition forType)
173 return HasDroppedNamespace(forType.Module);
176 public static bool HasDroppedNamespace(MemberReference forMember)
178 return HasDroppedNamespace(forMember.Module);
181 public static bool HasDroppedNamespace(AssemblyDefinition forAssembly)
183 return HasDroppedNamespace(forAssembly.MainModule);
186 public static bool HasDroppedNamespace(ModuleDefinition forModule)
188 return !string.IsNullOrWhiteSpace (droppedNamespace) && droppedAssemblies.Any(da => da == forModule.Name);
191 public static bool HasDroppedAnyNamespace ()
193 return !string.IsNullOrWhiteSpace (droppedNamespace);
197 static List<string> droppedAssemblies = new List<string>();
199 public string PreserveTag { get; set; }
200 public static MDocUpdater Instance { get; private set; }
201 public static bool SwitchingToMagicTypes { get; private set; }
203 public override void Run (IEnumerable<string> args)
206 show_exceptions = DebugOutput;
207 var types = new List<string> ();
208 var p = new OptionSet () {
210 "Delete removed members from the XML files.",
211 v => delete = v != null },
213 "Document potential exceptions that members can generate. {SOURCES} " +
214 "is a comma-separated list of:\n" +
215 " asm Method calls in same assembly\n" +
216 " depasm Method calls in dependent assemblies\n" +
217 " all Record all possible exceptions\n" +
218 " added Modifier; only create <exception/>s\n" +
219 " for NEW types/members\n" +
220 "If nothing is specified, then only exceptions from the member will " +
222 v => exceptions = ParseExceptionLocations (v) },
224 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
227 case "ignore-missing-types":
228 ignore_missing_types = true;
230 case "no-assembly-versions":
231 no_assembly_versions = true;
234 throw new Exception ("Unsupported flag `" + v + "'.");
237 { "fignore-missing-types",
238 "Do not report an error if a --type=TYPE type\nwas not found.",
239 v => ignore_missing_types = v != null },
240 { "fno-assembly-versions",
241 "Do not generate //AssemblyVersion elements.",
242 v => no_assembly_versions = v != null },
244 "Import documentation from {FILE}.",
245 v => AddImporter (v) },
247 "Check for assembly references in {DIRECTORY}.",
248 v => assemblyResolver.AddSearchDirectory (v) },
250 "Ignored for compatibility with update-ecma-xml.",
253 "Root {DIRECTORY} to generate/update documentation.",
256 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
257 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
258 v => assemblyResolver.AddSearchDirectory (Path.GetDirectoryName (v)) },
260 "Manually specify the assembly {VERSION} that new members were added in.",
263 "Only update documentation for {TYPE}.",
264 v => types.Add (v) },
266 "When processing assembly {ASSEMBLY}, strip off leading namespace {PREFIX}:\n" +
267 " e.g. --dropns ASSEMBLY=PREFIX",
269 var parts = v.Split ('=');
270 if (parts.Length != 2) { Console.Error.WriteLine ("Invalid dropns input"); return; }
271 var assembly = Path.GetFileName (parts [0].Trim ());
272 var prefix = parts [1].Trim();
273 droppedAssemblies.Add (assembly);
274 droppedNamespace = prefix;
277 "If the new assembly is switching to 'magic types', then this switch should be defined.",
278 v => SwitchingToMagicTypes = true },
280 "Do not delete members that don't exist in the assembly, but rather mark them as preserved.",
281 v => PreserveTag = "true" },
283 "Allow types to be in multiple assemblies.",
284 v => multiassembly = true },
286 var assemblies = Parse (p, args, "update",
287 "[OPTIONS]+ ASSEMBLIES",
288 "Create or update documentation from ASSEMBLIES.");
289 if (assemblies == null)
291 if (assemblies.Count == 0)
292 Error ("No assemblies specified.");
294 foreach (var dir in assemblies
295 .Where (a => a.Contains (Path.DirectorySeparatorChar))
296 .Select (a => Path.GetDirectoryName (a)))
297 assemblyResolver.AddSearchDirectory (dir);
299 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
302 throw new InvalidOperationException("The --out option is required.");
304 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
306 // Store types that have been forwarded to avoid duplicate generation
307 GatherForwardedTypes ();
309 docEnum = docEnum ?? new DocumentationEnumerator ();
311 // PERFORM THE UPDATES
313 if (types.Count > 0) {
315 DoUpdateTypes (srcPath, types, srcPath);
318 else if (opts.@namespace != null)
319 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
320 Path.Combine (dest_dir, opts.@namespace));
323 DoUpdateAssemblies (srcPath, srcPath);
325 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
327 public static bool IsInAssemblies(string name) {
328 var query = Instance.assemblies.Where (a => a.MainModule.Name == name).ToArray ();
329 return query.Length > 0;
331 void AddImporter (string path)
334 XmlReader r = new XmlTextReader (path);
336 while (r.NodeType != XmlNodeType.Element) {
338 Error ("Unable to read XML file: {0}.", path);
340 if (r.LocalName == "doc") {
341 importers.Add (new MsxdocDocumentationImporter (path));
343 else if (r.LocalName == "Libraries") {
344 var ecmadocs = new XmlTextReader (path);
345 docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
346 importers.Add (new EcmaDocumentationImporter (ecmadocs));
349 Error ("Unsupported XML format within {0}.", path);
352 } catch (Exception e) {
353 Environment.ExitCode = 1;
354 Error ("Could not load XML file: {0}.", e.Message);
358 void GatherForwardedTypes ()
360 foreach (var asm in assemblies)
361 foreach (var type in asm.MainModule.ExportedTypes.Where (t => t.IsForwarder).Select (t => t.FullName))
362 forwardedTypes.Add (type);
365 static ExceptionLocations ParseExceptionLocations (string s)
367 ExceptionLocations loc = ExceptionLocations.Member;
370 foreach (var type in s.Split (',')) {
372 case "added": loc |= ExceptionLocations.AddedMembers; break;
373 case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
374 case "asm": loc |= ExceptionLocations.Assembly; break;
375 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
376 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
382 internal void Warning (string format, params object[] args)
384 Message (TraceLevel.Warning, "mdoc: " + format, args);
387 private AssemblyDefinition LoadAssembly (string name)
389 AssemblyDefinition assembly = null;
391 assembly = AssemblyDefinition.ReadAssembly (name, new ReaderParameters { AssemblyResolver = assemblyResolver });
392 } catch (System.IO.FileNotFoundException) { }
394 if (assembly == null)
395 throw new InvalidOperationException("Assembly " + name + " not found.");
400 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
401 OrderTypeAttributes (element);
402 XmlTextWriter writer = new XmlTextWriter(output);
403 writer.Formatting = Formatting.Indented;
404 writer.Indentation = 2;
405 writer.IndentChar = ' ';
406 element.WriteTo(writer);
410 private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
412 Action<string> creator = file => {
413 using (var writer = OpenWrite (file, mode))
417 MdocFile.UpdateFile (filename, creator);
420 private static void OrderTypeAttributes (XmlElement e)
422 foreach (XmlElement type in e.SelectNodes ("//Type")) {
423 OrderTypeAttributes (type.Attributes);
427 static readonly string[] TypeAttributeOrder = {
428 "Name", "FullName", "FullNameSP", "Maintainer"
431 private static void OrderTypeAttributes (XmlAttributeCollection c)
433 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
434 for (int i = 0; i < c.Count; ++i) {
435 XmlAttribute a = c [i];
436 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
437 if (a.Name == TypeAttributeOrder [j]) {
443 for (int i = attrs.Length-1; i >= 0; --i) {
444 XmlAttribute n = attrs [i];
447 XmlAttribute r = null;
448 for (int j = i+1; j < attrs.Length; ++j) {
449 if (attrs [j] != null) {
456 if (c [n.Name] != null) {
457 c.RemoveNamedItem (n.Name);
458 c.InsertBefore (n, r);
463 private XmlDocument CreateIndexStub()
465 XmlDocument index = new XmlDocument();
467 XmlElement index_root = index.CreateElement("Overview");
468 index.AppendChild(index_root);
470 if (assemblies.Count == 0)
471 throw new Exception ("No assembly");
473 XmlElement index_assemblies = index.CreateElement("Assemblies");
474 index_root.AppendChild(index_assemblies);
476 XmlElement index_remarks = index.CreateElement("Remarks");
477 index_remarks.InnerText = "To be added.";
478 index_root.AppendChild(index_remarks);
480 XmlElement index_copyright = index.CreateElement("Copyright");
481 index_copyright.InnerText = "To be added.";
482 index_root.AppendChild(index_copyright);
484 XmlElement index_types = index.CreateElement("Types");
485 index_root.AppendChild(index_types);
490 private static void WriteNamespaceStub(string ns, string outdir) {
491 XmlDocument index = new XmlDocument();
493 XmlElement index_root = index.CreateElement("Namespace");
494 index.AppendChild(index_root);
496 index_root.SetAttribute("Name", ns);
498 XmlElement index_docs = index.CreateElement("Docs");
499 index_root.AppendChild(index_docs);
501 XmlElement index_summary = index.CreateElement("summary");
502 index_summary.InnerText = "To be added.";
503 index_docs.AppendChild(index_summary);
505 XmlElement index_remarks = index.CreateElement("remarks");
506 index_remarks.InnerText = "To be added.";
507 index_docs.AppendChild(index_remarks);
509 WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
510 writer => WriteXml (index.DocumentElement, writer));
513 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
515 var index = CreateIndexForTypes (dest);
517 var found = new HashSet<string> ();
518 foreach (AssemblyDefinition assembly in assemblies) {
519 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames)) {
520 string relpath = DoUpdateType (type, basepath, dest);
524 found.Add (type.FullName);
529 index.Add (assembly);
537 if (ignore_missing_types)
540 var notFound = from n in typenames where !found.Contains (n) select n;
542 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
545 class IndexForTypes {
551 XmlElement index_types;
552 XmlElement index_assemblies;
554 public IndexForTypes (MDocUpdater app, string indexFile, XmlDocument index)
557 this.indexFile = indexFile;
560 index_types = WriteElement (index.DocumentElement, "Types");
561 index_assemblies = WriteElement (index.DocumentElement, "Assemblies");
564 public void Add (AssemblyDefinition assembly)
566 if (index_assemblies.SelectSingleNode ("Assembly[@Name='" + assembly.Name.Name + "']") != null)
569 app.AddIndexAssembly (assembly, index_assemblies);
572 public void Add (TypeDefinition type)
574 app.AddIndexType (type, index_types);
579 SortIndexEntries (index_types);
580 WriteFile (indexFile, FileMode.Create,
581 writer => WriteXml (index.DocumentElement, writer));
585 IndexForTypes CreateIndexForTypes (string dest)
587 string indexFile = Path.Combine (dest, "index.xml");
588 if (File.Exists (indexFile))
590 return new IndexForTypes (this, indexFile, CreateIndexStub ());
593 /// <summary>Constructs the presumed path to the type's documentation file</summary>
594 /// <returns><c>true</c>, if the type file was found, <c>false</c> otherwise.</returns>
595 /// <param name="result">A typle that contains 1) the 'reltypefile', 2) the 'typefile', and 3) the file info</param>
596 bool TryFindTypeFile(string nsname, string typename, string basepath, out Tuple<string, string, FileInfo> result) {
597 string reltypefile = DocUtils.PathCombine (nsname, typename + ".xml");
598 string typefile = Path.Combine (basepath, reltypefile);
599 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
601 result = new Tuple<string, string, FileInfo> (reltypefile, typefile, file);
606 public string DoUpdateType (TypeDefinition type, string basepath, string dest)
608 if (type.Namespace == null)
609 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
611 if (!IsPublic (type))
614 // Must get the A+B form of the type name.
615 string typename = GetTypeFileName(type);
616 string nsname = DocUtils.GetNamespace (type);
618 // Find the file, if it exists
619 string[] searchLocations = new string[] {
623 if (MDocUpdater.HasDroppedNamespace (type)) {
624 // If dropping namespace, types may have moved into a couple of different places.
625 var newSearchLocations = searchLocations.Union (new string[] {
626 string.Format ("{0}.{1}", droppedNamespace, nsname),
627 nsname.Replace (droppedNamespace + ".", string.Empty),
628 MDocUpdater.droppedNamespace
631 searchLocations = newSearchLocations.ToArray ();
634 string reltypefile="", typefile="";
635 System.IO.FileInfo file = null;
637 foreach (var f in searchLocations) {
638 Tuple<string, string, FileInfo> result;
639 bool fileExists = TryFindTypeFile (f, typename, basepath, out result);
642 reltypefile = result.Item1;
643 typefile = result.Item2;
650 if (file == null || !file.Exists) {
651 // we were not able to find a file, let's use the original type informatio.
652 // so that we create the stub in the right place.
653 Tuple<string, string, FileInfo> result;
654 TryFindTypeFile (nsname, typename, basepath, out result);
656 reltypefile = result.Item1;
657 typefile = result.Item2;
661 string output = null;
664 } else if (dest == "-") {
667 output = Path.Combine (dest, reltypefile);
670 if (file != null && file.Exists) {
672 XmlDocument basefile = new XmlDocument();
674 basefile.Load(typefile);
675 } catch (Exception e) {
676 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
679 DoUpdateType2("Updating", basefile, type, output, false);
682 XmlElement td = StubType(type, output);
689 public void DoUpdateNS (string ns, string nspath, string outpath)
691 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
692 AssemblyDefinition assembly = assemblies [0];
694 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
695 XmlDocument basefile = new XmlDocument();
696 string typefile = Path.Combine(nspath, file.Name);
698 basefile.Load(typefile);
699 } catch (Exception e) {
700 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
704 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
705 TypeDefinition type = assembly.GetType(typename);
708 if (!string.IsNullOrWhiteSpace (droppedNamespace)) {
709 string nameWithNs = string.Format ("{0}.{1}", droppedNamespace, typename);
710 type = assembly.GetType (nameWithNs);
712 Warning ("Type no longer in assembly: " + typename);
719 seenTypes[type] = seenTypes;
720 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false);
723 // Stub types not in the directory
724 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
725 if (type.Namespace != ns || seenTypes.ContainsKey(type))
728 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"));
729 if (td == null) continue;
733 private static string GetTypeFileName (TypeReference type)
735 return filenameFormatter.GetName (type);
738 public static string GetTypeFileName (string typename)
740 StringBuilder filename = new StringBuilder (typename.Length);
744 for (int i = 0; i < typename.Length; ++i) {
745 char c = typename [i];
754 filename.Append ('`').Append ((numArgs+1).ToString());
769 return filename.ToString ();
772 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
774 XmlElement index_assembly = null;
776 index_assembly = (XmlElement)parent.SelectSingleNode ("Assembly[@Name='"+ assembly.Name.Name +"']");
778 if (index_assembly == null)
779 index_assembly = parent.OwnerDocument.CreateElement ("Assembly");
781 index_assembly.SetAttribute ("Name", assembly.Name.Name);
782 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
784 AssemblyNameDefinition name = assembly.Name;
785 if (name.HasPublicKey) {
786 XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
787 var key = new StringBuilder (name.PublicKey.Length*3 + 2);
789 foreach (byte b in name.PublicKey)
790 key.AppendFormat ("{0,2:x2} ", b);
792 pubkey.InnerText = key.ToString ();
793 index_assembly.AppendChild (pubkey);
796 if (!string.IsNullOrEmpty (name.Culture)) {
797 XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
798 culture.InnerText = name.Culture;
799 index_assembly.AppendChild (culture);
802 MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
803 parent.AppendChild(index_assembly);
806 private void AddIndexType (TypeDefinition type, XmlElement index_types)
808 string typename = GetTypeFileName(type);
810 // Add namespace and type nodes into the index file as needed
811 string ns = DocUtils.GetNamespace (type);
812 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode ("Namespace[@Name='" + ns + "']");
813 if (nsnode == null) {
814 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
815 nsnode.SetAttribute ("Name", ns);
816 index_types.AppendChild (nsnode);
818 string doc_typename = GetDocTypeName (type);
819 XmlElement typenode = (XmlElement) nsnode.SelectSingleNode ("Type[@Name='" + typename + "']");
820 if (typenode == null) {
821 typenode = index_types.OwnerDocument.CreateElement ("Type");
822 typenode.SetAttribute ("Name", typename);
823 nsnode.AppendChild (typenode);
825 if (typename != doc_typename)
826 typenode.SetAttribute("DisplayName", doc_typename);
828 typenode.RemoveAttribute("DisplayName");
830 typenode.SetAttribute ("Kind", GetTypeKind (type));
833 private void DoUpdateAssemblies (string source, string dest)
835 string indexfile = dest + "/index.xml";
837 if (System.IO.File.Exists(indexfile)) {
838 index = new XmlDocument();
839 index.Load(indexfile);
842 ClearElement(index.DocumentElement, "Assembly");
843 ClearElement(index.DocumentElement, "Attributes");
845 index = CreateIndexStub();
848 string defaultTitle = "Untitled";
849 if (assemblies.Count == 1)
850 defaultTitle = assemblies[0].Name.Name;
851 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
853 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
854 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
856 index_assemblies.RemoveAll ();
859 HashSet<string> goodfiles = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
861 foreach (AssemblyDefinition assm in assemblies) {
862 AddIndexAssembly (assm, index_assemblies);
863 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
866 SortIndexEntries (index_types);
868 CleanupFiles (dest, goodfiles);
869 CleanupIndexTypes (index_types, goodfiles);
870 CleanupExtensions (index_types);
872 WriteFile (indexfile, FileMode.Create,
873 writer => WriteXml(index.DocumentElement, writer));
876 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
878 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
880 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
881 string typename = GetTypeFileName(type);
882 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0 || forwardedTypes.Contains (type.FullName))
885 string reltypepath = DoUpdateType (type, source, dest);
886 if (reltypepath == null)
889 // Add namespace and type nodes into the index file as needed
890 AddIndexType (type, index_types);
892 // Ensure the namespace index file exists
893 string namespaceToUse = type.Namespace;
894 if (HasDroppedNamespace(assembly)) {
895 namespaceToUse = string.Format ("{0}.{1}", droppedNamespace, namespaceToUse);
897 string onsdoc = DocUtils.PathCombine (dest, namespaceToUse + ".xml");
898 string nsdoc = DocUtils.PathCombine (dest, "ns-" + namespaceToUse + ".xml");
899 if (File.Exists (onsdoc)) {
900 File.Move (onsdoc, nsdoc);
903 if (!File.Exists (nsdoc)) {
904 Console.WriteLine("New Namespace File: " + type.Namespace);
905 WriteNamespaceStub(namespaceToUse, dest);
908 goodfiles.Add (reltypepath);
912 private static void SortIndexEntries (XmlElement indexTypes)
914 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
915 XmlNodeComparer c = new AttributeNameComparer ();
916 SortXmlNodes (indexTypes, namespaces, c);
918 for (int i = 0; i < namespaces.Count; ++i)
919 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
922 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
924 MyXmlNodeList l = new MyXmlNodeList (children.Count);
925 for (int i = 0; i < children.Count; ++i)
926 l.Add (children [i]);
928 for (int i = l.Count - 1; i > 0; --i) {
929 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
933 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
935 public abstract int Compare (XmlNode x, XmlNode y);
937 public int Compare (object x, object y)
939 return Compare ((XmlNode) x, (XmlNode) y);
943 class AttributeNameComparer : XmlNodeComparer {
946 public AttributeNameComparer ()
951 public AttributeNameComparer (string attribute)
953 this.attribute = attribute;
956 public override int Compare (XmlNode x, XmlNode y)
958 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
962 class VersionComparer : XmlNodeComparer {
963 public override int Compare (XmlNode x, XmlNode y)
965 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
966 string a = GetVersion (x.InnerText);
967 string b = GetVersion (y.InnerText);
968 return new Version (a).CompareTo (new Version (b));
971 static string GetVersion (string v)
973 int n = v.IndexOf ("x");
976 return v.Substring (0, n-1);
980 private static string GetTypeKind (TypeDefinition type)
983 return "Enumeration";
984 if (type.IsValueType)
986 if (type.IsInterface)
988 if (DocUtils.IsDelegate (type))
990 if (type.IsClass || type.FullName == "System.Enum") // FIXME
992 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
995 public static bool IsPublic (TypeDefinition type)
997 TypeDefinition decl = type;
998 while (decl != null) {
999 if (!(decl.IsPublic || decl.IsNestedPublic ||
1000 decl.IsNestedFamily || decl.IsNestedFamily || decl.IsNestedFamilyOrAssembly)) {
1003 decl = (TypeDefinition) decl.DeclaringType;
1008 private void CleanupFiles (string dest, HashSet<string> goodfiles)
1010 // Look for files that no longer correspond to types
1011 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
1012 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
1013 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
1014 if (!goodfiles.Contains (relTypeFile)) {
1015 XmlDocument doc = new XmlDocument ();
1016 doc.Load (typefile.FullName);
1017 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
1018 string assemblyName = doc.SelectSingleNode ("/Type/AssemblyInfo/AssemblyName").InnerText;
1019 AssemblyDefinition assembly = assemblies.FirstOrDefault (a => a.Name.Name == assemblyName);
1021 Action saveDoc = () => {
1022 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
1023 WriteXml(doc.DocumentElement, writer);
1026 if (e != null && !no_assembly_versions && assembly != null && assemblyName != null && UpdateAssemblyVersions (e, assembly, GetAssemblyVersions(assemblyName), false)) {
1028 goodfiles.Add (relTypeFile);
1032 Action actuallyDelete = () => {
1033 string newname = typefile.FullName + ".remove";
1034 try { System.IO.File.Delete (newname); } catch (Exception) { Warning ("Unable to delete existing file: {0}", newname); }
1035 try { typefile.MoveTo (newname); } catch (Exception) { Warning ("Unable to rename to: {0}", newname); }
1036 Console.WriteLine ("Class no longer present; file renamed: " + Path.Combine (nsdir.Name, typefile.Name));
1039 if (string.IsNullOrWhiteSpace (PreserveTag)) { // only do this if there was not a -preserve
1042 var unifiedAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[@apistyle='unified']");
1043 var classicAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[@apistyle='classic']");
1044 var unifiedMembers = doc.SelectNodes ("//Member[@apistyle='unified']|//Member/AssemblyInfo[@apistyle='unified']");
1045 var classicMembers = doc.SelectNodes ("//Member[@apistyle='classic']|//Member/AssemblyInfo[@apistyle='classic']");
1046 bool isUnifiedRun = HasDroppedAnyNamespace ();
1047 bool isClassicOrNormalRun = !isUnifiedRun;
1049 Action<XmlNode, ApiStyle> removeStyles = (x, style) => {
1050 var styledNodes = doc.SelectNodes("//*[@apistyle='"+ style.ToString ().ToLowerInvariant () +"']");
1051 if (styledNodes != null && styledNodes.Count > 0) {
1052 foreach(var node in styledNodes.Cast<XmlNode> ()) {
1053 node.ParentNode.RemoveChild (node);
1058 if (isClassicOrNormalRun) {
1059 if (unifiedAssemblyNode != null || unifiedMembers.Count > 0) {
1060 Warning ("*** this type is marked as unified, not deleting during this run: {0}", typefile.FullName);
1061 // if truly removed from both assemblies, it will be removed fully during the unified run
1062 removeStyles (doc, ApiStyle.Classic);
1065 // we should be safe to delete here because it was not marked as a unified assembly
1070 if (classicAssemblyNode != null || classicMembers.Count > 0) {
1071 Warning ("*** this type is marked as classic, not deleting {0}", typefile.FullName);
1074 // safe to delete because it wasn't marked as a classic assembly, so the type is gone in both.
1084 private static TextWriter OpenWrite (string path, FileMode mode)
1086 var w = new StreamWriter (
1087 new FileStream (path, mode),
1088 new UTF8Encoding (false)
1094 private string[] GetAssemblyVersions (string assemblyName)
1096 return (from a in assemblies
1097 where a.Name.Name == assemblyName
1098 select GetAssemblyVersion (a)).ToArray ();
1101 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
1103 // Look for type nodes that no longer correspond to types
1104 MyXmlNodeList remove = new MyXmlNodeList ();
1105 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
1106 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
1107 if (!goodfiles.Contains (fulltypename)) {
1108 remove.Add (typenode);
1111 foreach (XmlNode n in remove)
1112 n.ParentNode.RemoveChild (n);
1115 private void CleanupExtensions (XmlElement index_types)
1117 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
1118 if (extensionMethods.Count == 0) {
1121 index_types.SelectSingleNode ("/Overview").RemoveChild (e);
1125 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
1126 index_types.SelectSingleNode ("/Overview").AppendChild (e);
1130 extensionMethods.Sort (DefaultExtensionMethodComparer);
1131 foreach (XmlNode m in extensionMethods) {
1132 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
1136 class ExtensionMethodComparer : XmlNodeComparer {
1137 public override int Compare (XmlNode x, XmlNode y)
1139 XmlNode xLink = x.SelectSingleNode ("Member/Link");
1140 XmlNode yLink = y.SelectSingleNode ("Member/Link");
1142 int n = xLink.Attributes ["Type"].Value.CompareTo (
1143 yLink.Attributes ["Type"].Value);
1146 n = xLink.Attributes ["Member"].Value.CompareTo (
1147 yLink.Attributes ["Member"].Value);
1148 if (n == 0 && !object.ReferenceEquals (x, y))
1149 throw new InvalidOperationException ("Duplicate extension method found!");
1154 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
1156 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince)
1158 Console.WriteLine(message + ": " + type.FullName);
1160 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
1162 // Update type metadata
1163 UpdateType(basefile.DocumentElement, type);
1165 // Update existing members. Delete member nodes that no longer should be there,
1166 // and remember what members are already documented so we don't add them again.
1168 MyXmlNodeList todelete = new MyXmlNodeList ();
1170 foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
1171 XmlElement oldmember = info.Node;
1172 MemberReference oldmember2 = info.Member;
1174 if (info.Member != null && info.Node != null) {
1175 // Check for an error condition where the xml MemberName doesn't match the matched member
1176 var memberName = GetMemberName (info.Member);
1177 var memberAttribute = info.Node.Attributes ["MemberName"];
1178 if (memberAttribute == null || (memberAttribute.Value != memberName && memberAttribute.Value.Split (',').Length != memberName.Split (',').Length)) {
1179 oldmember.SetAttribute ("MemberName", memberName);
1183 string sig = oldmember2 != null ? memberFormatters [0].GetDeclaration (oldmember2) : null;
1185 // Interface implementations and overrides are deleted from the docs
1186 // unless the overrides option is given.
1187 if (oldmember2 != null && sig == null)
1190 // Deleted (or signature changed)
1191 if (oldmember2 == null) {
1192 if (!no_assembly_versions && UpdateAssemblyVersions (oldmember, type.Module.Assembly, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
1195 DeleteMember ("Member Removed", output, oldmember, todelete, type);
1200 if (seenmembers.ContainsKey (sig)) {
1201 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
1202 // ignore, already seen
1204 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
1205 DeleteMember ("Duplicate Member Found", output, oldmember, todelete, type);
1207 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
1211 // Update signature information
1214 // get all apistyles of sig from info.Node
1215 var styles = oldmember.GetElementsByTagName ("MemberSignature").Cast<XmlElement> ()
1216 .Where (x => x.GetAttribute ("Language") == "C#" && !seenmembers.ContainsKey(x.GetAttribute("Value")))
1217 .Select (x => x.GetAttribute ("Value"));
1219 foreach (var stylesig in styles) {
1220 seenmembers.Add (stylesig, oldmember);
1223 foreach (XmlElement oldmember in todelete)
1224 oldmember.ParentNode.RemoveChild (oldmember);
1227 if (!DocUtils.IsDelegate (type)) {
1228 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
1229 var typemembers = type.GetMembers()
1231 if (m is TypeDefinition) return false;
1232 string sig = memberFormatters [0].GetDeclaration (m);
1233 if (sig == null) return false;
1234 if (seenmembers.ContainsKey(sig)) return false;
1236 // Verify that the member isn't an explicitly implemented
1237 // member of an internal interface, in which case we shouldn't return true.
1238 MethodDefinition methdef = null;
1239 if (m is MethodDefinition)
1240 methdef = m as MethodDefinition;
1241 else if (m is PropertyDefinition) {
1242 var prop = m as PropertyDefinition;
1243 methdef = prop.GetMethod ?? prop.SetMethod;
1246 if (methdef != null) {
1247 TypeReference iface;
1248 MethodReference imethod;
1250 if (methdef.Overrides.Count == 1) {
1251 DocUtils.GetInfoForExplicitlyImplementedMethod (methdef, out iface, out imethod);
1252 if (!IsPublic (iface.Resolve ())) return false;
1259 foreach (MemberReference m in typemembers) {
1260 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
1261 if (mm == null) continue;
1263 if (MDocUpdater.SwitchingToMagicTypes) {
1264 // this is a unified style API that obviously doesn't exist in the classic API. Let's mark
1265 // it with apistyle="unified", so that it's not displayed for classic style APIs
1266 mm.SetAttribute ("apistyle", "unified");
1269 members.AppendChild( mm );
1271 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
1276 // Import code snippets from files
1277 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
1278 if (!(code is XmlElement)) continue;
1279 string file = ((XmlElement)code).GetAttribute("src");
1280 string lang = ((XmlElement)code).GetAttribute("lang");
1282 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
1284 code.InnerText = src;
1288 if (insertSince && since != null) {
1289 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
1290 docs.AppendChild (CreateSinceNode (basefile));
1294 XmlElement d = basefile.DocumentElement ["Docs"];
1295 XmlElement m = basefile.DocumentElement ["Members"];
1296 if (d != null && m != null)
1297 basefile.DocumentElement.InsertBefore (
1298 basefile.DocumentElement.RemoveChild (d), m);
1299 SortTypeMembers (m);
1303 WriteXml(basefile.DocumentElement, Console.Out);
1305 FileInfo file = new FileInfo (output);
1306 if (!file.Directory.Exists) {
1307 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
1308 file.Directory.Create ();
1310 WriteFile (output, FileMode.Create,
1311 writer => WriteXml(basefile.DocumentElement, writer));
1315 private string GetCodeSource (string lang, string file)
1318 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
1319 // Grab the specified region
1320 string region = "#region " + file.Substring (anchorStart + 4);
1321 file = file.Substring (0, anchorStart + 3);
1323 using (StreamReader reader = new StreamReader (file)) {
1325 StringBuilder src = new StringBuilder ();
1327 while ((line = reader.ReadLine ()) != null) {
1328 if (line.Trim() == region) {
1329 indent = line.IndexOf (region);
1332 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
1337 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
1340 return src.ToString ();
1342 } catch (Exception e) {
1343 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
1344 file, region, show_exceptions ? e.ToString () : e.Message);
1349 using (StreamReader reader = new StreamReader (file))
1350 return reader.ReadToEnd ();
1351 } catch (Exception e) {
1352 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
1357 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete, TypeDefinition type)
1359 string format = output != null
1360 ? "{0}: File='{1}'; Signature='{4}'"
1361 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1362 string signature = member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value;
1366 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1367 member.Attributes ["MemberName"].Value,
1370 // Identify all of the different states that could affect our decision to delete the member
1371 bool shouldPreserve = !string.IsNullOrWhiteSpace (PreserveTag);
1372 bool hasContent = MemberDocsHaveUserContent (member);
1373 bool shouldDelete = !shouldPreserve && (delete || !hasContent);
1375 bool unifiedRun = HasDroppedNamespace (type);
1377 var classicAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[@apistyle='classic']");
1378 bool nodeIsClassic = classicAssemblyInfo != null || member.HasApiStyle (ApiStyle.Classic);
1379 var unifiedAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']");
1380 bool nodeIsUnified = unifiedAssemblyInfo != null || member.HasApiStyle (ApiStyle.Unified);
1382 Action actuallyDelete = () => {
1383 todelete.Add (member);
1387 if (!shouldDelete) {
1388 // explicitly not deleting
1389 string message = shouldPreserve ?
1390 "Not deleting '{0}' due to --preserve." :
1391 "Not deleting '{0}'; must be enabled with the --delete option";
1392 Warning (message, signature);
1393 } else if (unifiedRun && nodeIsClassic) {
1394 // this is a unified run, and the member doesn't exist, but is marked as being in the classic assembly.
1395 member.RemoveApiStyle (ApiStyle.Unified);
1396 Warning ("Not removing '{0}' since it's still in the classic assembly.", signature);
1397 } else if (unifiedRun && !nodeIsClassic) {
1398 // unified run, and the node is not classic, which means it doesn't exist anywhere.
1401 if (!nodeIsClassic && !nodeIsUnified) { // regular codepath (ie. not classic/unified)
1403 } else { // this is a classic run
1404 Warning ("Removing classic from '{0}' ... will be removed in the unified run if not present there.", signature);
1405 member.RemoveApiStyle (ApiStyle.Classic);
1406 if (classicAssemblyInfo != null) {
1407 member.RemoveChild (classicAssemblyInfo);
1413 class MemberComparer : XmlNodeComparer {
1414 public override int Compare (XmlNode x, XmlNode y)
1417 string xMemberName = x.Attributes ["MemberName"].Value;
1418 string yMemberName = y.Attributes ["MemberName"].Value;
1420 // generic methods *end* with '>'
1421 // it's possible for explicitly implemented generic interfaces to
1422 // contain <...> without being a generic method
1423 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1424 (r = xMemberName.CompareTo (yMemberName)) != 0)
1428 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1429 xMemberName = xMemberName.Substring (0, lt);
1430 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1431 yMemberName = yMemberName.Substring (0, lt);
1432 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1435 // if @MemberName matches, then it's either two different types of
1436 // members sharing the same name, e.g. field & property, or it's an
1437 // overloaded method.
1438 // for different type, sort based on MemberType value.
1439 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1440 y.SelectSingleNode ("MemberType").InnerText);
1444 // same type -- must be an overloaded method. Sort based on type
1445 // parameter count, then parameter count, then by the parameter
1447 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1448 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1449 if (xTypeParams.Count != yTypeParams.Count)
1450 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1451 for (int i = 0; i < xTypeParams.Count; ++i) {
1452 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1453 yTypeParams [i].Attributes ["Name"].Value);
1458 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1459 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1460 if (xParams.Count != yParams.Count)
1461 return xParams.Count <= yParams.Count ? -1 : 1;
1462 for (int i = 0; i < xParams.Count; ++i) {
1463 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1464 yParams [i].Attributes ["Type"].Value);
1468 // all parameters match, but return value might not match if it was
1469 // changed between one version and another.
1470 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1471 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1472 if (xReturn != null && yReturn != null) {
1473 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1482 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1484 private static void SortTypeMembers (XmlNode members)
1486 if (members == null)
1488 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1491 private static bool MemberDocsHaveUserContent (XmlNode e)
1493 e = (XmlElement)e.SelectSingleNode("Docs");
1494 if (e == null) return false;
1495 foreach (XmlElement d in e.SelectNodes("*"))
1496 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1501 // UPDATE HELPER FUNCTIONS
1503 // CREATE A STUB DOCUMENTATION FILE
1505 public XmlElement StubType (TypeDefinition type, string output)
1507 string typesig = typeFormatters [0].GetDeclaration (type);
1508 if (typesig == null) return null; // not publicly visible
1510 XmlDocument doc = new XmlDocument();
1511 XmlElement root = doc.CreateElement("Type");
1512 doc.AppendChild (root);
1514 DoUpdateType2 ("New Type", doc, type, output, true);
1519 private XmlElement CreateSinceNode (XmlDocument doc)
1521 XmlElement s = doc.CreateElement ("since");
1522 s.SetAttribute ("version", since);
1526 // STUBBING/UPDATING FUNCTIONS
1528 public void UpdateType (XmlElement root, TypeDefinition type)
1530 root.SetAttribute("Name", GetDocTypeName (type));
1531 root.SetAttribute("FullName", GetDocTypeFullName (type));
1533 foreach (MemberFormatter f in typeFormatters) {
1534 string element = "TypeSignature[@Language='" + f.Language + "']";
1535 string valueToUse = f.GetDeclaration (type);
1538 root.SelectNodes (element).Cast<XmlElement> ().ToArray (),
1539 x => x.GetAttribute ("Value") == valueToUse,
1540 x => x.SetAttribute ("Value", valueToUse),
1542 var node = WriteElementAttribute (root, element, "Language", f.Language, forceNewElement: true);
1543 var newnode = WriteElementAttribute (root, node, "Value", valueToUse);
1549 AddAssemblyNameToNode (root, type);
1551 string assemblyInfoNodeFilter = MDocUpdater.HasDroppedNamespace (type) ? "[@apistyle='unified']" : "[not(@apistyle) or @apistyle='classic']";
1552 Func<XmlElement, bool> assemblyFilter = x => x.SelectSingleNode ("AssemblyName").InnerText == type.Module.Assembly.Name.Name;
1553 foreach(var ass in root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ().Where (assemblyFilter))
1555 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1556 if (!no_assembly_versions) {
1557 UpdateAssemblyVersions (ass, type, true);
1560 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1561 foreach (var version in versions)
1562 ass.RemoveChild (version);
1564 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1565 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1567 ClearElement(ass, "AssemblyCulture");
1570 // Why-oh-why do we put assembly attributes in each type file?
1571 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1572 // since they're outdated in current docs, and a waste of space.
1573 //MakeAttributes(ass, type.Assembly, true);
1574 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1575 if (assattrs != null)
1576 ass.RemoveChild(assattrs);
1578 NormalizeWhitespace(ass);
1581 if (type.IsGenericType ()) {
1582 MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
1584 ClearElement(root, "TypeParameters");
1587 if (type.BaseType != null) {
1588 XmlElement basenode = WriteElement(root, "Base");
1590 string basetypename = GetDocTypeFullName (type.BaseType);
1591 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1592 WriteElementText(root, "Base/BaseTypeName", basetypename);
1594 // Document how this type instantiates the generic parameters of its base type
1595 TypeReference origBase = type.BaseType.GetElementType ();
1596 if (origBase.IsGenericType ()) {
1597 ClearElement(basenode, "BaseTypeArguments");
1598 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1599 IList<TypeReference> baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1600 IList<GenericParameter> baseGenParams = origBase.GenericParameters;
1601 if (baseGenArgs.Count != baseGenParams.Count)
1602 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1603 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1604 GenericParameter param = baseGenParams [i];
1605 TypeReference value = baseGenArgs [i];
1607 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1608 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1609 bta.AppendChild(arg);
1610 arg.SetAttribute ("TypeParamName", param.Name);
1611 arg.InnerText = GetDocTypeFullName (value);
1615 ClearElement(root, "Base");
1618 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1619 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1620 List<string> interface_names = userInterfaces
1621 .Select (iface => GetDocTypeFullName (iface))
1625 XmlElement interfaces = WriteElement(root, "Interfaces");
1626 interfaces.RemoveAll();
1627 foreach (string iname in interface_names) {
1628 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1629 interfaces.AppendChild(iface);
1630 WriteElementText(iface, "InterfaceName", iname);
1633 ClearElement(root, "Interfaces");
1636 MakeAttributes (root, GetCustomAttributes (type), type);
1638 if (DocUtils.IsDelegate (type)) {
1639 MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
1640 var member = type.GetMethod ("Invoke");
1641 MakeParameters(root, member, member.Parameters);
1642 MakeReturnValue(root, member);
1645 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1646 MakeDocNode (typeInfo);
1648 if (!DocUtils.IsDelegate (type))
1649 WriteElement (root, "Members");
1651 OrderTypeNodes (root, root.ChildNodes);
1652 NormalizeWhitespace(root);
1655 /// <summary>Adds an AssemblyInfo with AssemblyName node to an XmlElement.</summary>
1656 /// <returns>The assembly that was either added, or was already present</returns>
1657 static XmlElement AddAssemblyNameToNode (XmlElement root, TypeDefinition type)
1659 return AddAssemblyNameToNode (root, type.Module);
1662 /// <summary>Adds an AssemblyInfo with AssemblyName node to an XmlElement.</summary>
1663 /// <returns>The assembly that was either added, or was already present</returns>
1664 static XmlElement AddAssemblyNameToNode (XmlElement root, ModuleDefinition module)
1666 Func<XmlElement, bool> assemblyFilter = x => {
1667 var existingName = x.SelectSingleNode ("AssemblyName");
1668 return existingName != null && existingName.InnerText == module.Assembly.Name.Name;
1671 return AddAssemblyXmlNode (
1672 root.SelectNodes ("AssemblyInfo").Cast<XmlElement> ().ToArray (),
1673 assemblyFilter, x => WriteElementText (x, "AssemblyName", module.Assembly.Name.Name),
1675 XmlElement ass = WriteElement (root, "AssemblyInfo", forceNewElement: true);
1676 if (MDocUpdater.HasDroppedNamespace (module))
1677 ass.SetAttribute ("apistyle", "unified");
1682 static readonly string[] TypeNodeOrder = {
1686 "ThreadingSafetyStatement",
1687 "ThreadSafetyStatement",
1699 static void OrderTypeNodes (XmlNode member, XmlNodeList children)
1701 ReorderNodes (member, children, TypeNodeOrder);
1704 internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1706 List<T> l = new List<T> (list);
1711 private void UpdateMember (DocsNodeInfo info)
1713 XmlElement me = (XmlElement) info.Node;
1714 MemberReference mi = info.Member;
1716 foreach (MemberFormatter f in memberFormatters) {
1717 string element = "MemberSignature[@Language='" + f.Language + "']";
1719 var valueToUse = f.GetDeclaration (mi);
1722 me.SelectNodes (element).Cast<XmlElement> ().ToArray(),
1723 x => x.GetAttribute("Value") == valueToUse,
1724 x => x.SetAttribute ("Value", valueToUse),
1726 var node = WriteElementAttribute (me, element, "Language", f.Language, forceNewElement:true);
1727 var newNode = WriteElementAttribute (me, node, "Value", valueToUse);
1734 WriteElementText(me, "MemberType", GetMemberType(mi));
1736 if (!no_assembly_versions) {
1738 UpdateAssemblyVersions (me, mi, true);
1740 var node = AddAssemblyNameToNode (me, mi.Module);
1742 UpdateAssemblyVersionForAssemblyInfo (node, me, new[] { GetAssemblyVersion (mi.Module.Assembly) }, add: true);
1746 ClearElement (me, "AssemblyInfo");
1749 MakeAttributes (me, GetCustomAttributes (mi), mi.DeclaringType);
1751 MakeReturnValue(me, mi, MDocUpdater.HasDroppedNamespace(mi));
1752 if (mi is MethodReference) {
1753 MethodReference mb = (MethodReference) mi;
1754 if (mb.IsGenericMethod ())
1755 MakeTypeParameters (me, mb.GenericParameters, mi, MDocUpdater.HasDroppedNamespace(mi));
1757 MakeParameters(me, mi, MDocUpdater.HasDroppedNamespace(mi));
1760 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1761 WriteElementText(me, "MemberValue", fieldValue);
1763 info.Node = WriteElement (me, "Docs");
1765 OrderMemberNodes (me, me.ChildNodes);
1766 UpdateExtensionMethods (me, info);
1769 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, MemberReference member) {
1770 AddXmlNode (relevant, valueMatches, setValue, makeNewNode, member.Module);
1773 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, TypeDefinition type) {
1774 AddXmlNode (relevant, valueMatches, setValue, makeNewNode, type.Module);
1777 static XmlElement AddAssemblyXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, ModuleDefinition module)
1779 bool isUnified = MDocUpdater.HasDroppedNamespace (module);
1780 XmlElement thisAssemblyNode = relevant.FirstOrDefault (valueMatches);
1781 if (thisAssemblyNode == null) {
1782 thisAssemblyNode = makeNewNode ();
1783 setValue (thisAssemblyNode);
1787 thisAssemblyNode.AddApiStyle (ApiStyle.Unified);
1789 foreach (var otherNodes in relevant.Where (n => n != thisAssemblyNode && n.DoesNotHaveApiStyle (ApiStyle.Unified))) {
1790 otherNodes.AddApiStyle (ApiStyle.Classic);
1793 return thisAssemblyNode;
1796 /// <summary>Adds an xml node, reusing the node if it's available</summary>
1797 /// <param name="relevant">The existing set of nodes</param>
1798 /// <param name="valueMatches">Checks to see if the node's value matches what you're trying to write.</param>
1799 /// <param name="setValue">Sets the node's value</param>
1800 /// <param name="makeNewNode">Creates a new node, if valueMatches returns false.</param>
1801 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, ModuleDefinition module)
1803 bool shouldDuplicate = MDocUpdater.HasDroppedNamespace (module);
1804 var styleToUse = shouldDuplicate ? ApiStyle.Unified : ApiStyle.Classic;
1805 var existing = relevant;
1807 bool addedOldApiStyle = false;
1809 if (shouldDuplicate) {
1810 existing = existing.Where (n => n.HasApiStyle (styleToUse)).ToArray ();
1811 foreach (var n in relevant.Where (n => n.DoesNotHaveApiStyle (styleToUse))) {
1812 if (valueMatches (n)) {
1816 n.AddApiStyle (ApiStyle.Classic);
1817 addedOldApiStyle = true;
1822 if (!existing.Any ()) {
1823 var newNode = makeNewNode ();
1824 if (shouldDuplicate && addedOldApiStyle) {
1825 newNode.AddApiStyle (ApiStyle.Unified);
1829 var itemToReuse = existing.First ();
1830 setValue (itemToReuse);
1832 if (shouldDuplicate && addedOldApiStyle) {
1833 itemToReuse.AddApiStyle (styleToUse);
1839 static readonly string[] MemberNodeOrder = {
1854 static void OrderMemberNodes (XmlNode member, XmlNodeList children)
1856 ReorderNodes (member, children, MemberNodeOrder);
1859 static void ReorderNodes (XmlNode node, XmlNodeList children, string[] ordering)
1861 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1862 for (int i = 0; i < ordering.Length; ++i) {
1863 for (int j = 0; j < children.Count; ++j) {
1864 XmlNode c = children [j];
1865 if (c.Name == ordering [i]) {
1866 newChildren.Add (c);
1870 if (newChildren.Count >= 0)
1871 node.PrependChild ((XmlNode) newChildren [0]);
1872 for (int i = 1; i < newChildren.Count; ++i) {
1873 XmlNode prev = (XmlNode) newChildren [i-1];
1874 XmlNode cur = (XmlNode) newChildren [i];
1875 node.RemoveChild (cur);
1876 node.InsertAfter (cur, prev);
1880 IEnumerable<string> GetCustomAttributes (MemberReference mi)
1882 IEnumerable<string> attrs = Enumerable.Empty<string>();
1884 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1886 attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1888 PropertyDefinition pd = mi as PropertyDefinition;
1890 if (pd.GetMethod != null)
1891 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1892 if (pd.SetMethod != null)
1893 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1896 EventDefinition ed = mi as EventDefinition;
1898 if (ed.AddMethod != null)
1899 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1900 if (ed.RemoveMethod != null)
1901 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1907 IEnumerable<string> GetCustomAttributes (IList<CustomAttribute> attributes, string prefix)
1909 foreach (CustomAttribute attribute in attributes.OrderBy (ca => ca.AttributeType.FullName)) {
1911 TypeDefinition attrType = attribute.AttributeType as TypeDefinition;
1912 if (attrType != null && !IsPublic (attrType))
1914 if (slashdocFormatter.GetName (attribute.AttributeType) == null)
1917 if (Array.IndexOf (IgnorableAttributes, attribute.AttributeType.FullName) >= 0)
1920 StringList fields = new StringList ();
1922 for (int i = 0; i < attribute.ConstructorArguments.Count; ++i) {
1923 CustomAttributeArgument argument = attribute.ConstructorArguments [i];
1924 fields.Add (MakeAttributesValueString (
1929 (from namedArg in attribute.Fields
1930 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value })
1932 (from namedArg in attribute.Properties
1933 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value }))
1934 .OrderBy (v => v.Name);
1935 foreach (var d in namedArgs)
1936 fields.Add (string.Format ("{0}={1}", d.Name,
1937 MakeAttributesValueString (d.Value, d.Type)));
1939 string a2 = String.Join(", ", fields.ToArray ());
1940 if (a2 != "") a2 = "(" + a2 + ")";
1942 string name = attribute.GetDeclaringType();
1943 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1944 yield return prefix + name + a2;
1948 static readonly string[] ValidExtensionMembers = {
1957 static readonly string[] ValidExtensionDocMembers = {
1963 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1965 MethodDefinition me = info.Member as MethodDefinition;
1968 if (info.Parameters.Count < 1)
1970 if (!DocUtils.IsExtensionMethod (me))
1973 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1974 XmlNode member = e.CloneNode (true);
1975 em.AppendChild (member);
1976 RemoveExcept (member, ValidExtensionMembers);
1977 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1978 WriteElementText (member, "MemberType", "ExtensionMethod");
1979 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1980 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1981 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1982 member.AppendChild (link);
1983 AddTargets (em, info);
1985 extensionMethods.Add (em);
1988 private static void RemoveExcept (XmlNode node, string[] except)
1992 MyXmlNodeList remove = null;
1993 foreach (XmlNode n in node.ChildNodes) {
1994 if (Array.BinarySearch (except, n.Name) < 0) {
1996 remove = new MyXmlNodeList ();
2001 foreach (XmlNode n in remove)
2002 node.RemoveChild (n);
2005 private static void AddTargets (XmlNode member, DocsNodeInfo info)
2007 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
2008 member.PrependChild (targets);
2009 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
2010 AppendElementAttributeText (targets, "Target", "Type",
2011 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
2014 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
2015 IList<TypeReference> constraints = gp.Constraints;
2016 if (constraints.Count == 0)
2017 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
2019 foreach (TypeReference c in constraints)
2020 AppendElementAttributeText(targets, "Target", "Type",
2021 slashdocFormatter.GetDeclaration (c));
2025 private static bool GetFieldConstValue (FieldDefinition field, out string value)
2028 TypeDefinition type = field.DeclaringType.Resolve ();
2029 if (type != null && type.IsEnum) return false;
2031 if (type != null && type.IsGenericType ()) return false;
2032 if (!field.HasConstant)
2034 if (field.IsLiteral) {
2035 object val = field.Constant;
2036 if (val == null) value = "null";
2037 else if (val is Enum) value = val.ToString();
2038 else if (val is IFormattable) {
2039 value = ((IFormattable)val).ToString();
2041 value = "\"" + value + "\"";
2043 if (value != null && value != "")
2049 // XML HELPER FUNCTIONS
2051 internal static XmlElement WriteElement(XmlNode parent, string element, bool forceNewElement = false) {
2052 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
2053 if (ret == null || forceNewElement) {
2054 string[] path = element.Split('/');
2055 foreach (string p in path) {
2056 ret = (XmlElement)parent.SelectSingleNode(p);
2057 if (ret == null || forceNewElement) {
2059 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
2060 ename = ename.Substring(0, ename.IndexOf('['));
2061 ret = parent.OwnerDocument.CreateElement(ename);
2062 parent.AppendChild(ret);
2071 private static XmlElement WriteElementText(XmlNode parent, string element, string value, bool forceNewElement = false) {
2072 XmlElement node = WriteElement(parent, element, forceNewElement: forceNewElement);
2073 node.InnerText = value;
2077 static XmlElement AppendElementText (XmlNode parent, string element, string value)
2079 XmlElement n = parent.OwnerDocument.CreateElement (element);
2080 parent.AppendChild (n);
2081 n.InnerText = value;
2085 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
2087 XmlElement n = parent.OwnerDocument.CreateElement (element);
2088 parent.AppendChild (n);
2089 n.SetAttribute (attribute, value);
2093 internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
2095 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
2096 dest.AppendChild (copy);
2100 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
2101 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
2104 node = WriteElement(parent, element);
2105 node.InnerText = value;
2107 private static XmlElement WriteElementAttribute(XmlElement parent, string element, string attribute, string value, bool forceNewElement = false) {
2108 XmlElement node = WriteElement(parent, element, forceNewElement:forceNewElement);
2109 return WriteElementAttribute (parent, node, attribute, value);
2111 private static XmlElement WriteElementAttribute(XmlElement parent, XmlElement node, string attribute, string value) {
2112 if (node.GetAttribute (attribute) != value) {
2113 node.SetAttribute (attribute, value);
2117 internal static void ClearElement(XmlElement parent, string name) {
2118 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
2120 parent.RemoveChild(node);
2123 // DOCUMENTATION HELPER FUNCTIONS
2125 private void MakeDocNode (DocsNodeInfo info)
2127 List<GenericParameter> genericParams = info.GenericParameters;
2128 IList<ParameterDefinition> parameters = info.Parameters;
2129 TypeReference returntype = info.ReturnType;
2130 bool returnisreturn = info.ReturnIsReturn;
2131 XmlElement e = info.Node;
2132 bool addremarks = info.AddRemarks;
2134 WriteElementInitialText(e, "summary", "To be added.");
2136 if (parameters != null) {
2137 string[] values = new string [parameters.Count];
2138 for (int i = 0; i < values.Length; ++i)
2139 values [i] = parameters [i].Name;
2140 UpdateParameters (e, "param", values);
2143 if (genericParams != null) {
2144 string[] values = new string [genericParams.Count];
2145 for (int i = 0; i < values.Length; ++i)
2146 values [i] = genericParams [i].Name;
2147 UpdateParameters (e, "typeparam", values);
2150 string retnodename = null;
2151 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
2152 retnodename = returnisreturn ? "returns" : "value";
2153 string retnodename_other = !returnisreturn ? "returns" : "value";
2155 // If it has a returns node instead of a value node, change its name.
2156 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
2157 if (retother != null) {
2158 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
2159 foreach (XmlNode node in retother)
2160 retnode.AppendChild(node.CloneNode(true));
2161 e.ReplaceChild(retnode, retother);
2163 WriteElementInitialText(e, retnodename, "To be added.");
2166 ClearElement(e, "returns");
2167 ClearElement(e, "value");
2171 WriteElementInitialText(e, "remarks", "To be added.");
2173 if (exceptions.HasValue && info.Member != null &&
2174 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
2175 UpdateExceptions (e, info.Member);
2178 foreach (DocumentationImporter importer in importers)
2179 importer.ImportDocumentation (info);
2181 OrderDocsNodes (e, e.ChildNodes);
2182 NormalizeWhitespace(e);
2185 static readonly string[] DocsNodeOrder = {
2186 "typeparam", "param", "summary", "returns", "value", "remarks",
2189 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
2191 ReorderNodes (docs, children, DocsNodeOrder);
2195 private void UpdateParameters (XmlElement e, string element, string[] values)
2197 if (values != null) {
2198 XmlNode[] paramnodes = new XmlNode[values.Length];
2200 // Some documentation had param nodes with leading spaces.
2201 foreach (XmlElement paramnode in e.SelectNodes(element)){
2202 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
2205 // If a member has only one parameter, we can track changes to
2206 // the name of the parameter easily.
2207 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
2208 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
2211 bool reinsert = false;
2213 // Pick out existing and still-valid param nodes, and
2214 // create nodes for parameters not in the file.
2215 Hashtable seenParams = new Hashtable();
2216 for (int pi = 0; pi < values.Length; pi++) {
2217 string p = values [pi];
2220 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
2221 if (paramnodes[pi] != null) continue;
2223 XmlElement pe = e.OwnerDocument.CreateElement(element);
2224 pe.SetAttribute("name", p);
2225 pe.InnerText = "To be added.";
2226 paramnodes[pi] = pe;
2230 // Remove parameters that no longer exist and check all params are in the right order.
2232 MyXmlNodeList todelete = new MyXmlNodeList ();
2233 foreach (XmlElement paramnode in e.SelectNodes(element)) {
2234 string name = paramnode.GetAttribute("name");
2235 if (!seenParams.ContainsKey(name)) {
2236 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2237 Warning ("The following param node can only be deleted if the --delete option is given: ");
2238 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
2240 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
2241 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2245 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
2246 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2247 e.ParentNode.Attributes ["MemberName"].Value,
2250 Warning ("\tValue={0}", paramnode.OuterXml);
2252 todelete.Add (paramnode);
2257 if ((int)seenParams[name] != idx)
2263 foreach (XmlNode n in todelete) {
2264 n.ParentNode.RemoveChild (n);
2267 // Re-insert the parameter nodes at the top of the doc section.
2269 for (int pi = values.Length-1; pi >= 0; pi--)
2270 e.PrependChild(paramnodes[pi]);
2272 // Clear all existing param nodes
2273 foreach (XmlNode paramnode in e.SelectNodes(element)) {
2274 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2275 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
2276 Console.WriteLine(paramnode.OuterXml);
2278 paramnode.ParentNode.RemoveChild(paramnode);
2284 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
2286 string existingName = pe.GetAttribute ("name");
2287 pe.SetAttribute ("name", newName);
2288 if (existingName == newName)
2290 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
2291 if (paramref.GetAttribute ("name").Trim () == existingName)
2292 paramref.SetAttribute ("name", newName);
2295 class CrefComparer : XmlNodeComparer {
2297 public CrefComparer ()
2301 public override int Compare (XmlNode x, XmlNode y)
2303 string xType = x.Attributes ["cref"].Value;
2304 string yType = y.Attributes ["cref"].Value;
2305 string xNamespace = GetNamespace (xType);
2306 string yNamespace = GetNamespace (yType);
2308 int c = xNamespace.CompareTo (yNamespace);
2311 return xType.CompareTo (yType);
2314 static string GetNamespace (string type)
2316 int n = type.LastIndexOf ('.');
2318 return type.Substring (0, n);
2319 return string.Empty;
2323 private void UpdateExceptions (XmlNode docs, MemberReference member)
2325 string indent = new string (' ', 10);
2326 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
2327 string cref = slashdocFormatter.GetDeclaration (source.Exception);
2328 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
2331 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
2332 e.SetAttribute ("cref", cref);
2333 e.InnerXml = "To be added; from:\n" + indent + "<see cref=\"" +
2334 string.Join ("\" />,\n" + indent + "<see cref=\"",
2335 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
2336 .OrderBy (s => s)) +
2338 docs.AppendChild (e);
2340 SortXmlNodes (docs, docs.SelectNodes ("exception"),
2341 new CrefComparer ());
2344 private static void NormalizeWhitespace(XmlElement e) {
2345 // Remove all text and whitespace nodes from the element so it
2346 // is outputted with nice indentation and no blank lines.
2347 ArrayList deleteNodes = new ArrayList();
2348 foreach (XmlNode n in e)
2349 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2351 foreach (XmlNode n in deleteNodes)
2352 n.ParentNode.RemoveChild(n);
2355 private static bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
2357 TypeDefinition type = member as TypeDefinition;
2359 type = member.DeclaringType as TypeDefinition;
2361 var versions = new string[] { GetAssemblyVersion (type.Module.Assembly) };
2363 if (root.LocalName == "AssemblyInfo")
2364 return UpdateAssemblyVersionForAssemblyInfo (root, root.ParentNode as XmlElement, versions, add: true);
2366 return UpdateAssemblyVersions (root, type.Module.Assembly, versions, add);
2369 private static string GetAssemblyVersion (AssemblyDefinition assembly)
2371 return assembly.Name.Version.ToString();
2374 private static bool UpdateAssemblyVersions(XmlElement root, AssemblyDefinition assembly, string[] assemblyVersions, bool add)
2376 XmlElement av = (XmlElement) root.SelectSingleNode ("AssemblyVersions");
2378 // AssemblyVersions is not part of the spec
2379 root.RemoveChild (av);
2382 string oldNodeFilter = "AssemblyInfo[not(@apistyle) or @apistyle='classic']";
2383 string newNodeFilter = "AssemblyInfo[@apistyle='unified']";
2384 string thisNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? newNodeFilter : oldNodeFilter;
2385 string thatNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? oldNodeFilter : newNodeFilter;
2387 XmlElement e = (XmlElement) root.SelectSingleNode (thisNodeFilter);
2389 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2391 if (MDocUpdater.HasDroppedNamespace (assembly)) {
2392 e.SetAttribute ("apistyle", "unified");
2395 root.AppendChild(e);
2398 var thatNode = (XmlElement) root.SelectSingleNode (thatNodeFilter);
2399 if (MDocUpdater.HasDroppedNamespace (assembly) && thatNode != null) {
2400 // there's a classic node, we should add apistyles
2401 e.SetAttribute ("apistyle", "unified");
2402 thatNode.SetAttribute ("apistyle", "classic");
2405 return UpdateAssemblyVersionForAssemblyInfo (e, root, assemblyVersions, add);
2408 static bool UpdateAssemblyVersionForAssemblyInfo (XmlElement e, XmlElement root, string[] assemblyVersions, bool add)
2410 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().Where (v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0).ToList ();
2411 // matches.Count > 0 && add: ignore -- already present
2412 if (matches.Count > 0 && !add) {
2413 foreach (XmlNode c in matches)
2416 else if (matches.Count == 0 && add) {
2417 foreach (string sv in assemblyVersions) {
2418 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2424 // matches.Count == 0 && !add: ignore -- already not present
2425 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2426 SortXmlNodes (e, avs, new VersionComparer ());
2428 bool anyNodesLeft = avs.Count != 0;
2429 if (!anyNodesLeft) {
2430 e.ParentNode.RemoveChild (e);
2432 return anyNodesLeft;
2435 // FIXME: get TypeReferences instead of string comparison?
2436 private static string[] IgnorableAttributes = {
2437 // Security related attributes
2438 "System.Reflection.AssemblyKeyFileAttribute",
2439 "System.Reflection.AssemblyDelaySignAttribute",
2440 // Present in @RefType
2441 "System.Runtime.InteropServices.OutAttribute",
2442 // For naming the indexer to use when not using indexers
2443 "System.Reflection.DefaultMemberAttribute",
2444 // for decimal constants
2445 "System.Runtime.CompilerServices.DecimalConstantAttribute",
2446 // compiler generated code
2447 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
2448 // more compiler generated code, e.g. iterator methods
2449 "System.Diagnostics.DebuggerHiddenAttribute",
2450 "System.Runtime.CompilerServices.FixedBufferAttribute",
2451 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
2452 // extension methods
2453 "System.Runtime.CompilerServices.ExtensionAttribute",
2454 // Used to differentiate 'object' from C#4 'dynamic'
2455 "System.Runtime.CompilerServices.DynamicAttribute",
2458 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes, TypeReference t=null)
2460 if (!attributes.Any ()) {
2461 ClearElement (root, "Attributes");
2465 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2469 e = root.OwnerDocument.CreateElement("Attributes");
2471 foreach (string attribute in attributes) {
2472 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2475 WriteElementText(ae, "AttributeName", attribute);
2478 if (e.ParentNode == null)
2479 root.AppendChild(e);
2481 NormalizeWhitespace(e);
2484 public static string MakeAttributesValueString (object v, TypeReference valueType)
2486 var formatters = new [] {
2487 new AttributeValueFormatter (),
2488 new ApplePlatformEnumFormatter (),
2489 new StandardFlagsEnumFormatter (),
2490 new DefaultAttributeValueFormatter (),
2493 ResolvedTypeInfo type = new ResolvedTypeInfo (valueType);
2494 foreach (var formatter in formatters) {
2495 string formattedValue;
2496 if (formatter.TryFormatValue (v, type, out formattedValue)) {
2497 return formattedValue;
2501 // this should never occur because the DefaultAttributeValueFormatter will always
2502 // successfully format the value ... but this is needed to satisfy the compiler :)
2503 throw new InvalidDataException (string.Format ("Unable to format attribute value ({0})", v.ToString ()));
2506 internal static IDictionary<long, string> GetEnumerationValues (TypeDefinition type)
2508 var values = new Dictionary<long, string> ();
2510 (from f in type.Fields
2511 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
2513 values [ToInt64 (f.Constant)] = f.Name;
2518 internal static long ToInt64 (object value)
2521 return (long) (ulong) value;
2522 return Convert.ToInt64 (value);
2525 private void MakeParameters (XmlElement root, MemberReference member, IList<ParameterDefinition> parameters, bool shouldDuplicateWithNew=false)
2527 XmlElement e = WriteElement(root, "Parameters");
2530 foreach (ParameterDefinition p in parameters) {
2534 var ptype = GetDocParameterType (p.ParameterType);
2535 var newPType = ptype;
2537 if (MDocUpdater.SwitchingToMagicTypes) {
2538 newPType = NativeTypeManager.ConvertFromNativeType (ptype);
2541 // now find the existing node, if it's there so we can reuse it.
2542 var nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2543 .Cast<XmlElement> ().Where (x => x.GetAttribute ("Name") == p.Name)
2546 if (nodes.Count () == 0) {
2547 // wasn't found, let's make sure it wasn't just cause the param name was changed
2548 nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2549 .Cast<XmlElement> ()
2550 .Skip (i) // this makes sure we don't inadvertently "reuse" nodes when adding new ones
2551 .Where (x => x.GetAttribute ("Name") != p.Name && (x.GetAttribute ("Type") == ptype || x.GetAttribute ("Type") == newPType))
2552 .Take(1) // there might be more than one that meets this parameter ... only take the first.
2557 x => x.GetAttribute ("Type") == ptype,
2558 x => x.SetAttribute ("Type", ptype),
2560 pe = root.OwnerDocument.CreateElement ("Parameter");
2563 pe.SetAttribute ("Name", p.Name);
2564 pe.SetAttribute ("Type", ptype);
2565 if (p.ParameterType is ByReferenceType) {
2567 pe.SetAttribute ("RefType", "out");
2569 pe.SetAttribute ("RefType", "ref");
2572 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
2581 private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams, MemberReference member, bool shouldDuplicateWithNew)
2583 if (typeParams == null || typeParams.Count == 0) {
2584 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2586 root.RemoveChild (f);
2589 XmlElement e = WriteElement(root, "TypeParameters");
2591 var nodes = e.SelectNodes ("TypeParameter").Cast<XmlElement> ().ToArray ();
2593 foreach (GenericParameter t in typeParams) {
2595 IList<TypeReference> constraints = t.Constraints;
2596 GenericParameterAttributes attrs = t.Attributes;
2602 var baseType = e.SelectSingleNode("BaseTypeName");
2603 // TODO: should this comparison take into account BaseTypeName?
2604 return x.GetAttribute("Name") == t.Name;
2606 x => {}, // no additional action required
2609 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2611 pe.SetAttribute("Name", t.Name);
2612 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""), t.DeclaringType);
2613 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2614 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2622 ce = root.OwnerDocument.CreateElement ("Constraints");
2624 pe.AppendChild (ce);
2625 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2626 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2627 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2628 AppendElementText (ce, "ParameterAttribute", "Covariant");
2629 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2630 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2631 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2632 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2633 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2634 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2635 foreach (TypeReference c in constraints) {
2636 TypeDefinition cd = c.Resolve ();
2637 AppendElementText (ce,
2638 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2639 GetDocTypeFullName (c));
2648 private void MakeParameters (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew)
2650 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2651 MakeParameters (root, mi, ((MethodDefinition)mi).Parameters, shouldDuplicateWithNew);
2652 else if (mi is MethodDefinition) {
2653 MethodDefinition mb = (MethodDefinition) mi;
2654 IList<ParameterDefinition> parameters = mb.Parameters;
2655 MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
2656 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2657 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2658 p.SetAttribute ("RefType", "this");
2661 else if (mi is PropertyDefinition) {
2662 IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
2663 if (parameters.Count > 0)
2664 MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
2668 else if (mi is FieldDefinition) return;
2669 else if (mi is EventDefinition) return;
2670 else throw new ArgumentException();
2673 internal static string GetDocParameterType (TypeReference type)
2675 return GetDocTypeFullName (type).Replace ("@", "&");
2678 private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes, bool shouldDuplicateWithNew=false)
2680 XmlElement e = WriteElement(root, "ReturnValue");
2681 var valueToUse = GetDocTypeFullName (type);
2683 AddXmlNode (e.SelectNodes("ReturnType").Cast<XmlElement> ().ToArray (),
2684 x => x.InnerText == valueToUse,
2685 x => x.InnerText = valueToUse,
2687 var newNode = WriteElementText(e, "ReturnType", valueToUse, forceNewElement: true);
2688 if (attributes != null)
2689 MakeAttributes(e, GetCustomAttributes (attributes, ""), type);
2696 private void MakeReturnValue (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew=false)
2698 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2700 else if (mi is MethodDefinition)
2701 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes, shouldDuplicateWithNew);
2702 else if (mi is PropertyDefinition)
2703 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null, shouldDuplicateWithNew);
2704 else if (mi is FieldDefinition)
2705 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null, shouldDuplicateWithNew);
2706 else if (mi is EventDefinition)
2707 MakeReturnValue (root, ((EventDefinition)mi).EventType, null, shouldDuplicateWithNew);
2709 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2712 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2714 MemberReference mi = info.Member;
2715 if (mi is TypeDefinition) return null;
2717 string sigs = memberFormatters [0].GetDeclaration (mi);
2718 if (sigs == null) return null; // not publicly visible
2720 // no documentation for property/event accessors. Is there a better way of doing this?
2721 if (mi.Name.StartsWith("get_")) return null;
2722 if (mi.Name.StartsWith("set_")) return null;
2723 if (mi.Name.StartsWith("add_")) return null;
2724 if (mi.Name.StartsWith("remove_")) return null;
2725 if (mi.Name.StartsWith("raise_")) return null;
2727 XmlElement me = doc.CreateElement("Member");
2728 me.SetAttribute("MemberName", GetMemberName (mi));
2732 if (exceptions.HasValue &&
2733 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2734 UpdateExceptions (info.Node, info.Member);
2736 if (since != null) {
2737 XmlNode docs = me.SelectSingleNode("Docs");
2738 docs.AppendChild (CreateSinceNode (doc));
2744 internal static string GetMemberName (MemberReference mi)
2746 MethodDefinition mb = mi as MethodDefinition;
2748 PropertyDefinition pi = mi as PropertyDefinition;
2751 return DocUtils.GetPropertyName (pi);
2753 StringBuilder sb = new StringBuilder (mi.Name.Length);
2754 if (!DocUtils.IsExplicitlyImplemented (mb))
2755 sb.Append (mi.Name);
2757 TypeReference iface;
2758 MethodReference ifaceMethod;
2759 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2760 sb.Append (GetDocTypeFullName (iface));
2762 sb.Append (ifaceMethod.Name);
2764 if (mb.IsGenericMethod ()) {
2765 IList<GenericParameter> typeParams = mb.GenericParameters;
2766 if (typeParams.Count > 0) {
2768 sb.Append (typeParams [0].Name);
2769 for (int i = 1; i < typeParams.Count; ++i)
2770 sb.Append (",").Append (typeParams [i].Name);
2774 return sb.ToString ();
2777 /// SIGNATURE GENERATION FUNCTIONS
2778 internal static bool IsPrivate (MemberReference mi)
2780 return memberFormatters [0].GetDeclaration (mi) == null;
2783 internal static string GetMemberType (MemberReference mi)
2785 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2786 return "Constructor";
2787 if (mi is MethodDefinition)
2789 if (mi is PropertyDefinition)
2791 if (mi is FieldDefinition)
2793 if (mi is EventDefinition)
2795 throw new ArgumentException();
2798 private static string GetDocTypeName (TypeReference type)
2800 return docTypeFormatter.GetName (type);
2803 internal static string GetDocTypeFullName (TypeReference type)
2805 return DocTypeFullMemberFormatter.Default.GetName (type);
2808 internal static string GetXPathForMember (DocumentationMember member)
2810 StringBuilder xpath = new StringBuilder ();
2811 xpath.Append ("//Members/Member[@MemberName=\"")
2812 .Append (member.MemberName)
2814 if (member.Parameters != null && member.Parameters.Count > 0) {
2815 xpath.Append ("/Parameters[count(Parameter) = ")
2816 .Append (member.Parameters.Count);
2817 for (int i = 0; i < member.Parameters.Count; ++i) {
2818 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2819 xpath.Append (member.Parameters [i]);
2820 xpath.Append ("\"");
2822 xpath.Append ("]/..");
2824 return xpath.ToString ();
2827 public static string GetXPathForMember (XPathNavigator member)
2829 StringBuilder xpath = new StringBuilder ();
2830 xpath.Append ("//Type[@FullName=\"")
2831 .Append (member.SelectSingleNode ("../../@FullName").Value)
2833 xpath.Append ("Members/Member[@MemberName=\"")
2834 .Append (member.SelectSingleNode ("@MemberName").Value)
2836 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2837 if (parameters.Count > 0) {
2838 xpath.Append ("/Parameters[count(Parameter) = ")
2839 .Append (parameters.Count);
2841 while (parameters.MoveNext ()) {
2843 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2844 xpath.Append (parameters.Current.Value);
2845 xpath.Append ("\"");
2847 xpath.Append ("]/..");
2849 return xpath.ToString ();
2852 public static string GetXPathForMember (MemberReference member)
2854 StringBuilder xpath = new StringBuilder ();
2855 xpath.Append ("//Type[@FullName=\"")
2856 .Append (member.DeclaringType.FullName)
2858 xpath.Append ("Members/Member[@MemberName=\"")
2859 .Append (GetMemberName (member))
2862 IList<ParameterDefinition> parameters = null;
2863 if (member is MethodDefinition)
2864 parameters = ((MethodDefinition) member).Parameters;
2865 else if (member is PropertyDefinition) {
2866 parameters = ((PropertyDefinition) member).Parameters;
2868 if (parameters != null && parameters.Count > 0) {
2869 xpath.Append ("/Parameters[count(Parameter) = ")
2870 .Append (parameters.Count);
2871 for (int i = 0; i < parameters.Count; ++i) {
2872 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2873 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2874 xpath.Append ("\"");
2876 xpath.Append ("]/..");
2878 return xpath.ToString ();
2882 static class CecilExtensions {
2883 public static string GetDeclaringType(this CustomAttribute attribute)
2885 var type = attribute.Constructor.DeclaringType;
2886 var typeName = type.FullName;
2888 string translatedType = NativeTypeManager.GetTranslatedName (type);
2889 return translatedType;
2892 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
2894 foreach (var c in type.Methods.Where (m => m.IsConstructor))
2895 yield return (MemberReference) c;
2896 foreach (var e in type.Events)
2897 yield return (MemberReference) e;
2898 foreach (var f in type.Fields)
2899 yield return (MemberReference) f;
2900 foreach (var m in type.Methods.Where (m => !m.IsConstructor))
2901 yield return (MemberReference) m;
2902 foreach (var t in type.NestedTypes)
2903 yield return (MemberReference) t;
2904 foreach (var p in type.Properties)
2905 yield return (MemberReference) p;
2908 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
2910 return GetMembers (type).Where (m => m.Name == member);
2913 public static MemberReference GetMember (this TypeDefinition type, string member)
2915 return GetMembers (type, member).EnsureZeroOrOne ();
2918 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2920 if (source.Count () > 1)
2921 throw new InvalidOperationException ("too many matches");
2922 return source.FirstOrDefault ();
2925 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2928 .Where (m => m.Name == method)
2929 .EnsureZeroOrOne ();
2932 public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
2934 TypeDefinition def = type as TypeDefinition;
2936 return new MemberReference [0];
2937 CustomAttribute defMemberAttr = def.CustomAttributes
2938 .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
2939 if (defMemberAttr == null)
2940 return new MemberReference [0];
2941 string name = (string) defMemberAttr.ConstructorArguments [0].Value;
2942 return def.Properties
2943 .Where (p => p.Name == name)
2944 .Select (p => (MemberReference) p);
2947 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2949 return assembly.Modules.SelectMany (md => md.GetAllTypes ());
2952 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2954 return GetTypes (assembly)
2955 .Where (td => td.FullName == type)
2956 .EnsureZeroOrOne ();
2959 public static bool IsGenericType (this TypeReference type)
2961 return type.GenericParameters.Count > 0;
2964 public static bool IsGenericMethod (this MethodReference method)
2966 return method.GenericParameters.Count > 0;
2969 public static MemberReference Resolve (this MemberReference member)
2971 FieldReference fr = member as FieldReference;
2973 return fr.Resolve ();
2974 MethodReference mr = member as MethodReference;
2976 return mr.Resolve ();
2977 TypeReference tr = member as TypeReference;
2979 return tr.Resolve ();
2980 PropertyReference pr = member as PropertyReference;
2983 EventReference er = member as EventReference;
2986 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2989 public static TypeReference GetUnderlyingType (this TypeDefinition type)
2993 return type.Fields.First (f => f.Name == "value__").FieldType;
2996 public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
2998 return self.Types.SelectMany (t => t.GetAllTypes ());
3001 static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
3005 if (!self.HasNestedTypes)
3008 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
3018 static class DocUtils {
3020 public static bool DoesNotHaveApiStyle(this XmlElement element, ApiStyle style) {
3021 string styleString = style.ToString ().ToLowerInvariant ();
3022 string apistylevalue = element.GetAttribute ("apistyle");
3023 return apistylevalue != styleString || string.IsNullOrWhiteSpace(apistylevalue);
3025 public static bool HasApiStyle(this XmlElement element, ApiStyle style) {
3026 string styleString = style.ToString ().ToLowerInvariant ();
3027 return element.GetAttribute ("apistyle") == styleString;
3029 public static bool HasApiStyle(this XmlNode node, ApiStyle style)
3031 var attribute = node.Attributes ["apistyle"];
3032 return attribute != null && attribute.Value == style.ToString ().ToLowerInvariant ();
3034 public static void AddApiStyle(this XmlElement element, ApiStyle style) {
3035 string styleString = style.ToString ().ToLowerInvariant ();
3036 var existingValue = element.GetAttribute ("apistyle");
3037 if (string.IsNullOrWhiteSpace (existingValue) || existingValue != styleString) {
3038 element.SetAttribute ("apistyle", styleString);
3041 public static void RemoveApiStyle (this XmlElement element, ApiStyle style)
3043 string styleString = style.ToString ().ToLowerInvariant ();
3044 string existingValue = element.GetAttribute ("apistyle");
3045 if (string.IsNullOrWhiteSpace (existingValue) || existingValue == styleString) {
3046 element.RemoveAttribute ("apistyle");
3049 public static void RemoveApiStyle (this XmlNode node, ApiStyle style)
3051 var styleAttribute = node.Attributes ["apistyle"];
3052 if (styleAttribute != null && styleAttribute.Value == style.ToString ().ToLowerInvariant ()) {
3053 node.Attributes.Remove (styleAttribute);
3057 public static bool IsExplicitlyImplemented (MethodDefinition method)
3059 return method.IsPrivate && method.IsFinal && method.IsVirtual;
3062 public static string GetTypeDotMember (string name)
3064 int startType, startMethod;
3065 startType = startMethod = -1;
3066 for (int i = 0; i < name.Length; ++i) {
3067 if (name [i] == '.') {
3068 startType = startMethod;
3072 return name.Substring (startType+1);
3075 public static string GetMember (string name)
3077 int i = name.LastIndexOf ('.');
3080 return name.Substring (i+1);
3083 public static void GetInfoForExplicitlyImplementedMethod (
3084 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
3088 if (method.Overrides.Count != 1)
3089 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
3090 iface = method.Overrides [0].DeclaringType;
3091 ifaceMethod = method.Overrides [0];
3094 public static string GetPropertyName (PropertyDefinition pi)
3096 // Issue: (g)mcs-generated assemblies that explicitly implement
3097 // properties don't specify the full namespace, just the
3098 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
3099 MethodDefinition method = pi.GetMethod;
3101 method = pi.SetMethod;
3102 if (!IsExplicitlyImplemented (method))
3105 // Need to determine appropriate namespace for this member.
3106 TypeReference iface;
3107 MethodReference ifaceMethod;
3108 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3109 return string.Join (".", new string[]{
3110 DocTypeFullMemberFormatter.Default.GetName (iface),
3111 GetMember (pi.Name)});
3114 public static string GetNamespace (TypeReference type)
3116 if (type.GetElementType ().IsNested)
3117 type = type.GetElementType ();
3118 while (type != null && type.IsNested)
3119 type = type.DeclaringType;
3121 return string.Empty;
3123 string typeNS = type.Namespace;
3125 // first, make sure this isn't a type reference to another assembly/module
3127 bool isInAssembly = MDocUpdater.IsInAssemblies(type.Module.Name);
3128 if (isInAssembly && !typeNS.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (type)) {
3129 typeNS = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typeNS);
3134 public static string PathCombine (string dir, string path)
3140 return Path.Combine (dir, path);
3143 public static bool IsExtensionMethod (MethodDefinition method)
3146 method.CustomAttributes
3147 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
3148 && method.DeclaringType.CustomAttributes
3149 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
3152 public static bool IsDelegate (TypeDefinition type)
3154 TypeReference baseRef = type.BaseType;
3155 if (baseRef == null)
3157 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
3158 baseRef.FullName == "System.MulticastDelegate";
3161 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
3163 List<TypeReference> decls = new List<TypeReference> ();
3165 while (type.DeclaringType != null) {
3166 decls.Add (type.DeclaringType);
3167 type = type.DeclaringType;
3173 public static int GetGenericArgumentCount (TypeReference type)
3175 GenericInstanceType inst = type as GenericInstanceType;
3177 ? inst.GenericArguments.Count
3178 : type.GenericParameters.Count;
3181 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
3183 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
3184 List<TypeReference> userInterfaces = new List<TypeReference> ();
3185 foreach (TypeReference iface in type.Interfaces) {
3186 TypeReference lookup = iface.Resolve () ?? iface;
3187 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
3188 userInterfaces.Add (iface);
3190 return userInterfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()));
3193 private static string GetQualifiedTypeName (TypeReference type)
3195 return "[" + type.Scope.Name + "]" + type.FullName;
3198 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
3200 HashSet<string> inheritedInterfaces = new HashSet<string> ();
3201 Action<TypeDefinition> a = null;
3203 if (t == null) return;
3204 foreach (TypeReference r in t.Interfaces) {
3205 inheritedInterfaces.Add (GetQualifiedTypeName (r));
3209 TypeReference baseRef = type.BaseType;
3210 while (baseRef != null) {
3211 TypeDefinition baseDef = baseRef.Resolve ();
3212 if (baseDef != null) {
3214 baseRef = baseDef.BaseType;
3219 foreach (TypeReference r in type.Interfaces)
3221 return inheritedInterfaces;
3225 class DocsNodeInfo {
3226 public DocsNodeInfo (XmlElement node)
3231 public DocsNodeInfo (XmlElement node, TypeDefinition type)
3237 public DocsNodeInfo (XmlElement node, MemberReference member)
3240 SetMemberInfo (member);
3243 void SetType (TypeDefinition type)
3246 throw new ArgumentNullException ("type");
3248 GenericParameters = new List<GenericParameter> (type.GenericParameters);
3249 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
3250 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
3251 for (int i = 0; i < declTypes.Count - 1; ++i) {
3252 int remove = System.Math.Min (maxGenArgs,
3253 DocUtils.GetGenericArgumentCount (declTypes [i]));
3254 maxGenArgs -= remove;
3255 while (remove-- > 0)
3256 GenericParameters.RemoveAt (0);
3258 if (DocUtils.IsDelegate (type)) {
3259 Parameters = type.GetMethod("Invoke").Parameters;
3260 ReturnType = type.GetMethod("Invoke").ReturnType;
3261 ReturnIsReturn = true;
3265 void SetMemberInfo (MemberReference member)
3268 throw new ArgumentNullException ("member");
3269 ReturnIsReturn = true;
3273 if (member is MethodReference ) {
3274 MethodReference mr = (MethodReference) member;
3275 Parameters = mr.Parameters;
3276 if (mr.IsGenericMethod ()) {
3277 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
3280 else if (member is PropertyDefinition) {
3281 Parameters = ((PropertyDefinition) member).Parameters;
3284 if (member is MethodDefinition) {
3285 ReturnType = ((MethodDefinition) member).ReturnType;
3286 } else if (member is PropertyDefinition) {
3287 ReturnType = ((PropertyDefinition) member).PropertyType;
3288 ReturnIsReturn = false;
3291 // no remarks section for enum members
3292 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
3296 public TypeReference ReturnType;
3297 public List<GenericParameter> GenericParameters;
3298 public IList<ParameterDefinition> Parameters;
3299 public bool ReturnIsReturn;
3300 public XmlElement Node;
3301 public bool AddRemarks = true;
3302 public MemberReference Member;
3303 public TypeDefinition Type;
3305 public override string ToString ()
3307 return string.Format ("{0} - {1} - {2}", Type, Member, Node == null ? "no xml" : "with xml");
3311 class DocumentationEnumerator {
3313 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3315 return GetDocumentationTypes (assembly, forTypes, null);
3318 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3320 foreach (TypeDefinition type in assembly.GetTypes()) {
3321 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
3323 if (seen != null && seen.Contains (type.FullName))
3326 foreach (TypeDefinition nested in type.NestedTypes)
3327 yield return nested;
3331 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3333 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
3334 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
3335 oldmember.RemoveAttribute ("__monodocer-seen__");
3338 MemberReference m = GetMember (type, new DocumentationMember (oldmember));
3340 yield return new DocsNodeInfo (oldmember);
3343 yield return new DocsNodeInfo (oldmember, m);
3348 protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
3350 string membertype = member.MemberType;
3352 string returntype = member.ReturnType;
3354 string docName = member.MemberName;
3356 string[] docTypeParams = GetTypeParameters (docName, member.TypeParameters);
3358 // If we're using 'magic types', then we might get false positives ... in those cases, we keep searching
3359 MemberReference likelyCandidate = null;
3361 // Loop through all members in this type with the same name
3362 var reflectedMembers = GetReflectionMembers (type, docName).ToArray ();
3363 foreach (MemberReference mi in reflectedMembers) {
3364 bool matchedMagicType = false;
3365 if (mi is TypeDefinition) continue;
3366 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
3368 if (MDocUpdater.IsPrivate (mi))
3371 IList<ParameterDefinition> pis = null;
3372 string[] typeParams = null;
3373 if (mi is MethodDefinition) {
3374 MethodDefinition mb = (MethodDefinition) mi;
3375 pis = mb.Parameters;
3376 if (mb.IsGenericMethod ()) {
3377 IList<GenericParameter> args = mb.GenericParameters;
3378 typeParams = args.Select (p => p.Name).ToArray ();
3381 else if (mi is PropertyDefinition)
3382 pis = ((PropertyDefinition)mi).Parameters;
3384 // check type parameters
3385 int methodTcount = member.TypeParameters == null ? 0 : member.TypeParameters.Count;
3386 int reflectionTcount = typeParams == null ? 0 : typeParams.Length;
3387 if (methodTcount != reflectionTcount)
3390 // check member parameters
3391 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
3392 int pcount = pis == null ? 0 : pis.Count;
3393 if (mcount != pcount)
3396 MethodDefinition mDef = mi as MethodDefinition;
3397 if (mDef != null && !mDef.IsConstructor) {
3398 // Casting operators can overload based on return type.
3399 string rtype = GetReplacedString (
3400 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
3401 typeParams, docTypeParams);
3402 string originalRType = rtype;
3403 if (MDocUpdater.SwitchingToMagicTypes) {
3404 rtype = NativeTypeManager.ConvertFromNativeType (rtype);
3407 if ((returntype != rtype && originalRType == rtype) ||
3408 (MDocUpdater.SwitchingToMagicTypes && returntype != originalRType && returntype != rtype && originalRType != rtype)) {
3412 if (originalRType != rtype)
3413 matchedMagicType = true;
3419 for (int i = 0; i < pis.Count; i++) {
3420 string paramType = GetReplacedString (
3421 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
3422 typeParams, docTypeParams);
3424 // if magictypes, replace paramType to "classic value" ... so the comparison works
3425 string originalParamType = paramType;
3426 if (MDocUpdater.SwitchingToMagicTypes) {
3427 paramType = NativeTypeManager.ConvertFromNativeType (paramType);
3430 string xmlMemberType = member.Parameters [i];
3431 if ((!paramType.Equals(xmlMemberType) && paramType.Equals(originalParamType)) ||
3432 (MDocUpdater.SwitchingToMagicTypes && !originalParamType.Equals(xmlMemberType) && !paramType.Equals(xmlMemberType) && !paramType.Equals(originalParamType))) {
3434 // did not match ... if we're dropping the namespace, and the paramType has the dropped
3435 // namespace, we should see if it matches when added
3436 bool stillDoesntMatch = true;
3437 if (MDocUpdater.HasDroppedNamespace(type) && paramType.StartsWith (MDocUpdater.droppedNamespace)) {
3438 string withDroppedNs = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, xmlMemberType);
3440 stillDoesntMatch = withDroppedNs != paramType;
3443 if (stillDoesntMatch) {
3449 if (originalParamType != paramType)
3450 matchedMagicType = true;
3452 if (!good) continue;
3454 if (MDocUpdater.SwitchingToMagicTypes && likelyCandidate == null && matchedMagicType) {
3455 // 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
3456 likelyCandidate = mi;
3463 return likelyCandidate;
3466 static string[] GetTypeParameters (string docName, IEnumerable<string> knownParameters)
3468 if (docName [docName.Length-1] != '>')
3470 StringList types = new StringList ();
3471 int endToken = docName.Length-2;
3472 int i = docName.Length-2;
3474 if (docName [i] == ',' || docName [i] == '<') {
3475 types.Add (docName.Substring (i + 1, endToken - i));
3478 if (docName [i] == '<')
3483 var arrayTypes = types.ToArray ();
3485 if (knownParameters != null && knownParameters.Any () && arrayTypes.Length != knownParameters.Count ())
3486 return knownParameters.ToArray ();
3491 protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
3493 // In case of dropping the namespace, we have to remove the dropped NS
3494 // so that docName will match what's in the assembly/type
3495 if (MDocUpdater.HasDroppedNamespace (type) && docName.StartsWith(MDocUpdater.droppedNamespace + ".")) {
3496 int droppedNsLength = MDocUpdater.droppedNamespace.Length;
3497 docName = docName.Substring (droppedNsLength + 1, docName.Length - droppedNsLength - 1);
3500 // need to worry about 4 forms of //@MemberName values:
3501 // 1. "Normal" (non-generic) member names: GetEnumerator
3503 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
3504 // - try as-is, and try type.member (due to "kludge" for property
3506 // 3. "Normal" Generic member names: Sort<T> (CSC)
3507 // - need to remove generic parameters --> "Sort"
3508 // 4. Explicitly-implemented interface members for generic interfaces:
3509 // -- System.Collections.Generic.IEnumerable<T>.Current
3510 // - Try as-is, and try type.member, *keeping* the generic parameters.
3511 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
3512 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
3513 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
3514 // this as (1) or (2).
3515 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
3517 foreach (MemberReference mi in type.GetMembers (docName))
3519 if (CountChars (docName, '.') > 0)
3520 // might be a property; try only type.member instead of
3521 // namespace.type.member.
3522 foreach (MemberReference mi in
3523 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
3530 int startLt, startType, startMethod;
3531 startLt = startType = startMethod = -1;
3532 for (int i = 0; i < docName.Length; ++i) {
3533 switch (docName [i]) {
3542 if (numLt == 0 && (i + 1) < docName.Length)
3543 // there's another character in docName, so this <...> sequence is
3544 // probably part of a generic type -- case 4.
3548 startType = startMethod;
3554 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
3556 foreach (MemberReference mi in type.GetMembers (refName))
3560 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
3563 // If we _still_ haven't found it, we've hit another generic naming issue:
3564 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
3565 // explicitly-implemented METHOD names (not properties), e.g.
3566 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
3567 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
3568 // which the XML docs will contain.
3570 // Alas, we can't derive the Mono name from docName, so we need to iterate
3571 // over all member names, convert them into CSC format, and compare... :-(
3574 foreach (MemberReference mi in type.GetMembers ()) {
3575 if (MDocUpdater.GetMemberName (mi) == docName)
3580 static string GetReplacedString (string typeName, string[] from, string[] to)
3584 for (int i = 0; i < from.Length; ++i)
3585 typeName = typeName.Replace (from [i], to [i]);
3589 private static int CountChars (string s, char c)
3592 for (int i = 0; i < s.Length; ++i) {
3600 class EcmaDocumentationEnumerator : DocumentationEnumerator {
3605 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
3608 this.ecmadocs = ecmaDocs;
3611 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3613 HashSet<string> seen = new HashSet<string> ();
3614 return GetDocumentationTypes (assembly, forTypes, seen)
3615 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
3618 new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3621 while (ecmadocs.Read ()) {
3622 switch (ecmadocs.Name) {
3624 if (typeDepth == -1)
3625 typeDepth = ecmadocs.Depth;
3626 if (ecmadocs.NodeType != XmlNodeType.Element)
3628 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
3630 string typename = ecmadocs.GetAttribute ("FullName");
3631 string typename2 = MDocUpdater.GetTypeFileName (typename);
3632 if (forTypes != null &&
3633 forTypes.BinarySearch (typename) < 0 &&
3634 typename != typename2 &&
3635 forTypes.BinarySearch (typename2) < 0)
3638 if ((t = assembly.GetType (typename)) == null &&
3639 (t = assembly.GetType (typename2)) == null)
3641 seen.Add (typename);
3642 if (typename != typename2)
3643 seen.Add (typename2);
3644 Console.WriteLine (" Import: {0}", t.FullName);
3645 if (ecmadocs.Name != "Docs") {
3646 int depth = ecmadocs.Depth;
3647 while (ecmadocs.Read ()) {
3648 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
3652 if (!ecmadocs.IsStartElement ("Docs"))
3653 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
3663 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3665 return GetMembers (basefile, type)
3666 .Concat (base.GetDocumentationMembers (basefile, type));
3669 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
3671 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
3674 if (ecmadocs.IsEmptyElement)
3677 int membersDepth = ecmadocs.Depth;
3679 while (go && ecmadocs.Read ()) {
3680 switch (ecmadocs.Name) {
3682 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
3684 DocumentationMember dm = new DocumentationMember (ecmadocs);
3686 string xp = MDocUpdater.GetXPathForMember (dm);
3687 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
3689 if (oldmember == null) {
3690 m = GetMember (type, dm);
3692 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3693 type.FullName, dm.MemberSignatures ["C#"]);
3694 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
3697 // oldmember lookup may have failed due to type parameter renames.
3699 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
3700 if (oldmember == null) {
3701 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
3702 oldmember = basefile.CreateElement ("Member");
3703 oldmember.SetAttribute ("MemberName", dm.MemberName);
3704 members.AppendChild (oldmember);
3705 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
3706 XmlElement ms = basefile.CreateElement ("MemberSignature");
3707 ms.SetAttribute ("Language", key);
3708 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
3709 oldmember.AppendChild (ms);
3711 oldmember.SetAttribute ("__monodocer-seen__", "true");
3712 Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
3717 m = GetMember (type, new DocumentationMember (oldmember));
3719 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3720 type.FullName, dm.MemberSignatures ["C#"]);
3723 oldmember.SetAttribute ("__monodocer-seen__", "true");
3725 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
3726 if (ecmadocs.Name != "Docs")
3727 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
3732 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
3741 abstract class DocumentationImporter {
3743 public abstract void ImportDocumentation (DocsNodeInfo info);
3746 class MsxdocDocumentationImporter : DocumentationImporter {
3748 XmlDocument slashdocs;
3750 public MsxdocDocumentationImporter (string file)
3752 var xml = File.ReadAllText (file);
3754 // Ensure Unix line endings
3755 xml = xml.Replace ("\r", "");
3757 slashdocs = new XmlDocument();
3758 slashdocs.LoadXml (xml);
3761 public override void ImportDocumentation (DocsNodeInfo info)
3763 XmlNode elem = GetDocs (info.Member ?? info.Type);
3768 XmlElement e = info.Node;
3770 if (elem.SelectSingleNode("summary") != null)
3771 MDocUpdater.ClearElement(e, "summary");
3772 if (elem.SelectSingleNode("remarks") != null)
3773 MDocUpdater.ClearElement(e, "remarks");
3774 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3775 MDocUpdater.ClearElement(e, "value");
3776 MDocUpdater.ClearElement(e, "returns");
3779 foreach (XmlNode child in elem.ChildNodes) {
3780 switch (child.Name) {
3783 XmlAttribute name = child.Attributes ["name"];
3786 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3788 p2.InnerXml = child.InnerXml;
3791 // Occasionally XML documentation will use <returns/> on
3792 // properties, so let's try to normalize things.
3795 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3796 v.InnerXml = child.InnerXml;
3802 case "permission": {
3803 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3806 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3808 a = e.OwnerDocument.CreateElement (child.Name);
3809 a.SetAttribute ("cref", cref.Value);
3812 a.InnerXml = child.InnerXml;
3816 XmlAttribute cref = child.Attributes ["cref"];
3819 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3821 a = e.OwnerDocument.CreateElement ("altmember");
3822 a.SetAttribute ("cref", cref.Value);
3829 if (child.NodeType == XmlNodeType.Element &&
3830 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3833 MDocUpdater.CopyNode (child, e);
3840 private XmlNode GetDocs (MemberReference member)
3842 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3843 if (slashdocsig != null)
3844 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3849 class EcmaDocumentationImporter : DocumentationImporter {
3853 public EcmaDocumentationImporter (XmlReader ecmaDocs)
3855 this.ecmadocs = ecmaDocs;
3858 public override void ImportDocumentation (DocsNodeInfo info)
3860 if (!ecmadocs.IsStartElement ("Docs")) {
3864 XmlElement e = info.Node;
3866 int depth = ecmadocs.Depth;
3867 ecmadocs.ReadStartElement ("Docs");
3868 while (ecmadocs.Read ()) {
3869 if (ecmadocs.Name == "Docs") {
3870 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3873 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3875 if (!ecmadocs.IsStartElement ())
3877 switch (ecmadocs.Name) {
3880 string name = ecmadocs.GetAttribute ("name");
3883 XmlNode doc = e.SelectSingleNode (
3884 ecmadocs.Name + "[@name='" + name + "']");
3885 string value = ecmadocs.ReadInnerXml ();
3887 doc.InnerXml = value.Replace ("\r", "");
3894 string name = ecmadocs.Name;
3895 string cref = ecmadocs.GetAttribute ("cref");
3898 XmlNode doc = e.SelectSingleNode (
3899 ecmadocs.Name + "[@cref='" + cref + "']");
3900 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3902 doc.InnerXml = value;
3904 XmlElement n = e.OwnerDocument.CreateElement (name);
3905 n.SetAttribute ("cref", cref);
3912 string name = ecmadocs.Name;
3913 string xpath = ecmadocs.Name;
3914 StringList attributes = new StringList (ecmadocs.AttributeCount);
3915 if (ecmadocs.MoveToFirstAttribute ()) {
3917 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3918 } while (ecmadocs.MoveToNextAttribute ());
3919 ecmadocs.MoveToContent ();
3921 if (attributes.Count > 0) {
3922 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3924 XmlNode doc = e.SelectSingleNode (xpath);
3925 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3927 doc.InnerXml = value;
3930 XmlElement n = e.OwnerDocument.CreateElement (name);
3932 foreach (string a in attributes) {
3933 int eq = a.IndexOf ('=');
3934 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3945 class DocumentationMember {
3946 public StringToStringMap MemberSignatures = new StringToStringMap ();
3947 public string ReturnType;
3948 public StringList Parameters;
3949 public StringList TypeParameters;
3950 public string MemberName;
3951 public string MemberType;
3953 public DocumentationMember (XmlReader reader)
3955 MemberName = reader.GetAttribute ("MemberName");
3956 int depth = reader.Depth;
3958 StringList p = new StringList ();
3959 StringList tp = new StringList ();
3961 if (reader.NodeType != XmlNodeType.Element)
3964 bool shouldUse = true;
3966 string apistyle = reader.GetAttribute ("apistyle");
3967 shouldUse = string.IsNullOrWhiteSpace(apistyle) || apistyle == "classic"; // only use this tag if it's an 'classic' style node
3969 catch (Exception ex) {}
3970 switch (reader.Name) {
3971 case "MemberSignature":
3973 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3977 MemberType = reader.ReadElementString ();
3980 if (reader.Depth == depth + 2 && shouldUse)
3981 ReturnType = reader.ReadElementString ();
3984 if (reader.Depth == depth + 2 && shouldUse)
3985 p.Add (reader.GetAttribute ("Type"));
3987 case "TypeParameter":
3988 if (reader.Depth == depth + 2 && shouldUse)
3989 tp.Add (reader.GetAttribute ("Name"));
3992 if (reader.Depth == depth + 1)
3996 } while (go && reader.Read () && reader.Depth >= depth);
4001 TypeParameters = tp;
4003 DiscernTypeParameters ();
4007 public DocumentationMember (XmlNode node)
4009 MemberName = node.Attributes ["MemberName"].Value;
4010 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
4011 XmlAttribute l = n.Attributes ["Language"];
4012 XmlAttribute v = n.Attributes ["Value"];
4013 XmlAttribute apistyle = n.Attributes ["apistyle"];
4014 bool shouldUse = apistyle == null || apistyle.Value == "classic";
4015 if (l != null && v != null && shouldUse)
4016 MemberSignatures [l.Value] = v.Value;
4018 MemberType = node.SelectSingleNode ("MemberType").InnerText;
4019 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType[not(@apistyle) or @apistyle='classic']");
4021 ReturnType = rt.InnerText;
4022 XmlNodeList p = node.SelectNodes ("Parameters/Parameter[not(@apistyle) or @apistyle='classic']");
4024 Parameters = new StringList (p.Count);
4025 for (int i = 0; i < p.Count; ++i)
4026 Parameters.Add (p [i].Attributes ["Type"].Value);
4028 XmlNodeList tp = node.SelectNodes ("TypeParameters/TypeParameter[not(@apistyle) or @apistyle='classic']");
4030 TypeParameters = new StringList (tp.Count);
4031 for (int i = 0; i < tp.Count; ++i)
4032 TypeParameters.Add (tp [i].Attributes ["Name"].Value);
4035 DiscernTypeParameters ();
4039 void DiscernTypeParameters ()
4041 // see if we can discern the param list from the name
4042 if (MemberName.Contains ("<") && MemberName.EndsWith (">")) {
4043 var starti = MemberName.IndexOf ("<") + 1;
4044 var endi = MemberName.LastIndexOf (">");
4045 var paramlist = MemberName.Substring (starti, endi - starti);
4046 var tparams = paramlist.Split (new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
4047 TypeParameters = new StringList (tparams);
4052 public class DynamicParserContext {
4053 public ReadOnlyCollection<bool> TransformFlags;
4054 public int TransformIndex;
4056 public DynamicParserContext (ICustomAttributeProvider provider)
4059 if (provider.HasCustomAttributes &&
4060 (da = (provider.CustomAttributes.Cast<CustomAttribute>()
4061 .SingleOrDefault (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))) != null) {
4062 CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
4063 ? new CustomAttributeArgument [0]
4064 : (CustomAttributeArgument[]) da.ConstructorArguments [0].Value;
4066 TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool) t.Value).ToArray());
4071 public enum MemberFormatterState {
4073 WithinGenericTypeParameters,
4076 public abstract class MemberFormatter {
4078 public virtual string Language {
4082 public string GetName (MemberReference member)
4084 return GetName (member, null);
4087 public virtual string GetName (MemberReference member, DynamicParserContext context)
4089 TypeReference type = member as TypeReference;
4091 return GetTypeName (type, context);
4092 MethodReference method = member as MethodReference;
4093 if (method != null && method.Name == ".ctor") // method.IsConstructor
4094 return GetConstructorName (method);
4096 return GetMethodName (method);
4097 PropertyReference prop = member as PropertyReference;
4099 return GetPropertyName (prop);
4100 FieldReference field = member as FieldReference;
4102 return GetFieldName (field);
4103 EventReference e = member as EventReference;
4105 return GetEventName (e);
4106 throw new NotSupportedException ("Can't handle: " +
4107 (member == null ? "null" : member.GetType().ToString()));
4110 protected virtual string GetTypeName (TypeReference type)
4112 return GetTypeName (type, null);
4115 protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
4118 throw new ArgumentNullException ("type");
4119 return _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
4122 protected virtual char[] ArrayDelimeters {
4123 get {return new char[]{'[', ']'};}
4126 protected virtual MemberFormatterState MemberFormatterState { get; set; }
4128 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4130 if (type is ArrayType) {
4131 TypeSpecification spec = type as TypeSpecification;
4132 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
4133 return AppendArrayModifiers (buf, (ArrayType) type);
4135 if (type is ByReferenceType) {
4136 return AppendRefTypeName (buf, type, context);
4138 if (type is PointerType) {
4139 return AppendPointerTypeName (buf, type, context);
4141 if (type is GenericParameter) {
4142 return AppendTypeName (buf, type, context);
4144 AppendNamespace (buf, type);
4145 GenericInstanceType genInst = type as GenericInstanceType;
4146 if (type.GenericParameters.Count == 0 &&
4147 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
4148 return AppendFullTypeName (buf, type, context);
4150 return AppendGenericType (buf, type, context);
4153 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4155 string ns = DocUtils.GetNamespace (type);
4156 if (ns != null && ns.Length > 0)
4157 buf.Append (ns).Append ('.');
4161 protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4163 if (type.DeclaringType != null)
4164 AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
4165 return AppendTypeName (buf, type, context);
4168 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4170 if (context != null)
4171 context.TransformIndex++;
4172 return AppendTypeName (buf, type.Name);
4175 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
4177 int n = typename.IndexOf ("`");
4179 return buf.Append (typename.Substring (0, n));
4180 return buf.Append (typename);
4183 protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
4185 buf.Append (ArrayDelimeters [0]);
4186 int rank = array.Rank;
4188 buf.Append (new string (',', rank-1));
4189 return buf.Append (ArrayDelimeters [1]);
4192 protected virtual string RefTypeModifier {
4196 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4198 TypeSpecification spec = type as TypeSpecification;
4199 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4200 .Append (RefTypeModifier);
4203 protected virtual string PointerModifier {
4207 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4209 TypeSpecification spec = type as TypeSpecification;
4210 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4211 .Append (PointerModifier);
4214 protected virtual char[] GenericTypeContainer {
4215 get {return new char[]{'<', '>'};}
4218 protected virtual char NestedTypeSeparator {
4222 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4224 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4225 type is GenericInstanceType ? type.GetElementType () : type);
4226 List<TypeReference> genArgs = GetGenericArguments (type);
4229 bool insertNested = false;
4230 foreach (var decl in decls) {
4231 TypeReference declDef = decl.Resolve () ?? decl;
4233 buf.Append (NestedTypeSeparator);
4235 insertNested = true;
4236 AppendTypeName (buf, declDef, context);
4237 int ac = DocUtils.GetGenericArgumentCount (declDef);
4241 buf.Append (GenericTypeContainer [0]);
4242 var origState = MemberFormatterState;
4243 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4244 _AppendTypeName (buf, genArgs [argIdx++], context);
4245 for (int i = 1; i < c; ++i) {
4246 _AppendTypeName (buf.Append (","), genArgs [argIdx++], context);
4248 MemberFormatterState = origState;
4249 buf.Append (GenericTypeContainer [1]);
4255 protected List<TypeReference> GetGenericArguments (TypeReference type)
4257 var args = new List<TypeReference> ();
4258 GenericInstanceType inst = type as GenericInstanceType;
4260 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
4262 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
4266 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4271 protected virtual string GetConstructorName (MethodReference constructor)
4273 return constructor.Name;
4276 protected virtual string GetMethodName (MethodReference method)
4281 protected virtual string GetPropertyName (PropertyReference property)
4283 return property.Name;
4286 protected virtual string GetFieldName (FieldReference field)
4291 protected virtual string GetEventName (EventReference e)
4296 public virtual string GetDeclaration (MemberReference member)
4299 throw new ArgumentNullException ("member");
4300 TypeDefinition type = member as TypeDefinition;
4302 return GetTypeDeclaration (type);
4303 MethodDefinition method = member as MethodDefinition;
4304 if (method != null && method.IsConstructor)
4305 return GetConstructorDeclaration (method);
4307 return GetMethodDeclaration (method);
4308 PropertyDefinition prop = member as PropertyDefinition;
4310 return GetPropertyDeclaration (prop);
4311 FieldDefinition field = member as FieldDefinition;
4313 return GetFieldDeclaration (field);
4314 EventDefinition e = member as EventDefinition;
4316 return GetEventDeclaration (e);
4317 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
4320 protected virtual string GetTypeDeclaration (TypeDefinition type)
4323 throw new ArgumentNullException ("type");
4324 StringBuilder buf = new StringBuilder (type.Name.Length);
4325 _AppendTypeName (buf, type, null);
4326 AppendGenericTypeConstraints (buf, type);
4327 return buf.ToString ();
4330 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
4332 return GetConstructorName (constructor);
4335 protected virtual string GetMethodDeclaration (MethodDefinition method)
4337 if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
4338 ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
4341 // Special signature for destructors.
4342 if (method.Name == "Finalize" && method.Parameters.Count == 0)
4343 return GetFinalizerName (method);
4345 StringBuilder buf = new StringBuilder ();
4347 AppendVisibility (buf, method);
4348 if (buf.Length == 0 &&
4349 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
4352 AppendModifiers (buf, method);
4354 if (buf.Length != 0)
4356 buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
4358 AppendMethodName (buf, method);
4359 AppendGenericMethod (buf, method).Append (" ");
4360 AppendParameters (buf, method, method.Parameters);
4361 AppendGenericMethodConstraints (buf, method);
4362 return buf.ToString ();
4365 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4367 return buf.Append (method.Name);
4370 protected virtual string GetFinalizerName (MethodDefinition method)
4375 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4380 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4385 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4390 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4395 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4400 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
4402 return GetPropertyName (property);
4405 protected virtual string GetFieldDeclaration (FieldDefinition field)
4407 return GetFieldName (field);
4410 protected virtual string GetEventDeclaration (EventDefinition e)
4412 return GetEventName (e);
4416 class ILFullMemberFormatter : MemberFormatter {
4418 public override string Language {
4419 get {return "ILAsm";}
4422 protected override char NestedTypeSeparator {
4428 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4430 if (GetBuiltinType (type.FullName) != null)
4432 string ns = DocUtils.GetNamespace (type);
4433 if (ns != null && ns.Length > 0) {
4434 if (type.IsValueType)
4435 buf.Append ("valuetype ");
4437 buf.Append ("class ");
4438 buf.Append (ns).Append ('.');
4443 protected static string GetBuiltinType (string t)
4446 case "System.Byte": return "unsigned int8";
4447 case "System.SByte": return "int8";
4448 case "System.Int16": return "int16";
4449 case "System.Int32": return "int32";
4450 case "System.Int64": return "int64";
4451 case "System.IntPtr": return "native int";
4453 case "System.UInt16": return "unsigned int16";
4454 case "System.UInt32": return "unsigned int32";
4455 case "System.UInt64": return "unsigned int64";
4456 case "System.UIntPtr": return "native unsigned int";
4458 case "System.Single": return "float32";
4459 case "System.Double": return "float64";
4460 case "System.Boolean": return "bool";
4461 case "System.Char": return "char";
4462 case "System.Void": return "void";
4463 case "System.String": return "string";
4464 case "System.Object": return "object";
4469 protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
4471 return buf.Append (typename);
4474 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4476 if (type is GenericParameter) {
4477 AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
4481 string s = GetBuiltinType (type.FullName);
4483 return buf.Append (s);
4485 return base.AppendTypeName (buf, type, context);
4488 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
4490 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
4491 return buf.Append (type.Owner is TypeReference ? "!" : "!!");
4493 GenericParameterAttributes attrs = type.Attributes;
4494 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
4495 buf.Append ("class ");
4496 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
4497 buf.Append ("struct ");
4498 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
4499 buf.Append (".ctor ");
4500 IList<TypeReference> constraints = type.Constraints;
4501 MemberFormatterState = 0;
4502 if (constraints.Count > 0) {
4503 var full = new ILFullMemberFormatter ();
4504 buf.Append ("(").Append (full.GetName (constraints [0]));
4505 for (int i = 1; i < constraints.Count; ++i) {
4506 buf.Append (", ").Append (full.GetName (constraints [i]));
4510 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4512 if ((attrs & GenericParameterAttributes.Covariant) != 0)
4514 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
4519 protected override string GetTypeDeclaration (TypeDefinition type)
4521 string visibility = GetTypeVisibility (type.Attributes);
4522 if (visibility == null)
4525 StringBuilder buf = new StringBuilder ();
4527 buf.Append (".class ");
4529 buf.Append ("nested ");
4530 buf.Append (visibility).Append (" ");
4531 if (type.IsInterface)
4532 buf.Append ("interface ");
4533 if (type.IsSequentialLayout)
4534 buf.Append ("sequential ");
4535 if (type.IsAutoLayout)
4536 buf.Append ("auto ");
4537 if (type.IsAnsiClass)
4538 buf.Append ("ansi ");
4539 if (type.IsAbstract)
4540 buf.Append ("abstract ");
4541 if (type.IsSerializable)
4542 buf.Append ("serializable ");
4544 buf.Append ("sealed ");
4545 if (type.IsBeforeFieldInit)
4546 buf.Append ("beforefieldinit ");
4547 var state = MemberFormatterState;
4548 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4549 buf.Append (GetName (type));
4550 MemberFormatterState = state;
4551 var full = new ILFullMemberFormatter ();
4552 if (type.BaseType != null) {
4553 buf.Append (" extends ");
4554 if (type.BaseType.FullName == "System.Object")
4555 buf.Append ("System.Object");
4557 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
4560 foreach (var name in type.Interfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()))
4561 .Select (i => full.GetName (i))
4562 .OrderBy (n => n)) {
4564 buf.Append (" implements ");
4573 return buf.ToString ();
4576 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4578 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4579 type is GenericInstanceType ? type.GetElementType () : type);
4581 foreach (var decl in decls) {
4582 TypeReference declDef = decl.Resolve () ?? decl;
4584 buf.Append (NestedTypeSeparator);
4587 AppendTypeName (buf, declDef, context);
4591 foreach (TypeReference arg in GetGenericArguments (type)) {
4595 _AppendTypeName (buf, arg, context);
4601 static string GetTypeVisibility (TypeAttributes ta)
4603 switch (ta & TypeAttributes.VisibilityMask) {
4604 case TypeAttributes.Public:
4605 case TypeAttributes.NestedPublic:
4608 case TypeAttributes.NestedFamily:
4609 case TypeAttributes.NestedFamORAssem:
4617 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4619 return GetMethodDeclaration (constructor);
4622 protected override string GetMethodDeclaration (MethodDefinition method)
4624 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
4627 var buf = new StringBuilder ();
4628 buf.Append (".method ");
4629 AppendVisibility (buf, method);
4630 if (method.IsStatic)
4631 buf.Append ("static ");
4632 if (method.IsHideBySig)
4633 buf.Append ("hidebysig ");
4634 if (method.IsPInvokeImpl) {
4635 var info = method.PInvokeInfo;
4636 buf.Append ("pinvokeimpl (\"")
4637 .Append (info.Module.Name)
4638 .Append ("\" as \"")
4639 .Append (info.EntryPoint)
4641 if (info.IsCharSetAuto)
4642 buf.Append (" auto");
4643 if (info.IsCharSetUnicode)
4644 buf.Append (" unicode");
4645 if (info.IsCharSetAnsi)
4646 buf.Append (" ansi");
4647 if (info.IsCallConvCdecl)
4648 buf.Append (" cdecl");
4649 if (info.IsCallConvStdCall)
4650 buf.Append (" stdcall");
4651 if (info.IsCallConvWinapi)
4652 buf.Append (" winapi");
4653 if (info.IsCallConvThiscall)
4654 buf.Append (" thiscall");
4655 if (info.SupportsLastError)
4656 buf.Append (" lasterr");
4659 if (method.IsSpecialName)
4660 buf.Append ("specialname ");
4661 if (method.IsRuntimeSpecialName)
4662 buf.Append ("rtspecialname ");
4663 if (method.IsNewSlot)
4664 buf.Append ("newslot ");
4665 if (method.IsVirtual)
4666 buf.Append ("virtual ");
4667 if (!method.IsStatic)
4668 buf.Append ("instance ");
4669 _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
4671 .Append (method.Name);
4672 if (method.IsGenericMethod ()) {
4673 var state = MemberFormatterState;
4674 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4675 IList<GenericParameter> args = method.GenericParameters;
4676 if (args.Count > 0) {
4678 _AppendTypeName (buf, args [0], null);
4679 for (int i = 1; i < args.Count; ++i)
4680 _AppendTypeName (buf.Append (", "), args [i], null);
4683 MemberFormatterState = state;
4688 for (int i = 0; i < method.Parameters.Count; ++i) {
4692 _AppendTypeName (buf, method.Parameters [i].ParameterType, new DynamicParserContext (method.Parameters [i]));
4694 buf.Append (method.Parameters [i].Name);
4698 buf.Append (" cil");
4699 if (method.IsRuntime)
4700 buf.Append (" runtime");
4701 if (method.IsManaged)
4702 buf.Append (" managed");
4704 return buf.ToString ();
4707 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4709 if (DocUtils.IsExplicitlyImplemented (method)) {
4710 TypeReference iface;
4711 MethodReference ifaceMethod;
4712 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4713 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4715 .Append (ifaceMethod.Name);
4717 return base.AppendMethodName (buf, method);
4720 protected override string RefTypeModifier {
4724 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4726 if (method.IsPublic)
4727 return buf.Append ("public ");
4728 if (method.IsFamilyAndAssembly)
4729 return buf.Append ("familyandassembly");
4730 if (method.IsFamilyOrAssembly)
4731 return buf.Append ("familyorassembly");
4732 if (method.IsFamily)
4733 return buf.Append ("family");
4737 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4739 string modifiers = String.Empty;
4740 if (method.IsStatic) modifiers += " static";
4741 if (method.IsVirtual && !method.IsAbstract) {
4742 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4743 else modifiers += " override";
4745 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4746 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4747 if (method.IsFinal) modifiers += " sealed";
4748 if (modifiers == " virtual sealed") modifiers = "";
4750 return buf.Append (modifiers);
4753 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4755 if (method.IsGenericMethod ()) {
4756 IList<GenericParameter> args = method.GenericParameters;
4757 if (args.Count > 0) {
4759 buf.Append (args [0].Name);
4760 for (int i = 1; i < args.Count; ++i)
4761 buf.Append (",").Append (args [i].Name);
4768 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4770 return AppendParameters (buf, method, parameters, '(', ')');
4773 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4777 if (parameters.Count > 0) {
4778 if (DocUtils.IsExtensionMethod (method))
4779 buf.Append ("this ");
4780 AppendParameter (buf, parameters [0]);
4781 for (int i = 1; i < parameters.Count; ++i) {
4783 AppendParameter (buf, parameters [i]);
4787 return buf.Append (end);
4790 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4792 if (parameter.ParameterType is ByReferenceType) {
4793 if (parameter.IsOut)
4794 buf.Append ("out ");
4796 buf.Append ("ref ");
4798 buf.Append (GetName (parameter.ParameterType)).Append (" ");
4799 return buf.Append (parameter.Name);
4802 protected override string GetPropertyDeclaration (PropertyDefinition property)
4804 MethodDefinition gm = null, sm = null;
4806 string get_visible = null;
4807 if ((gm = property.GetMethod) != null &&
4808 (DocUtils.IsExplicitlyImplemented (gm) ||
4809 (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
4810 get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
4811 string set_visible = null;
4812 if ((sm = property.SetMethod) != null &&
4813 (DocUtils.IsExplicitlyImplemented (sm) ||
4814 (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
4815 set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
4817 if ((set_visible == null) && (get_visible == null))
4820 StringBuilder buf = new StringBuilder ()
4821 .Append (".property ");
4822 if (!(gm ?? sm).IsStatic)
4823 buf.Append ("instance ");
4824 _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
4825 buf.Append (' ').Append (property.Name);
4826 if (!property.HasParameters || property.Parameters.Count == 0)
4827 return buf.ToString ();
4831 foreach (ParameterDefinition p in property.Parameters) {
4835 _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
4839 return buf.ToString ();
4842 protected override string GetFieldDeclaration (FieldDefinition field)
4844 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4845 if (declType.IsEnum && field.Name == "value__")
4846 return null; // This member of enums aren't documented.
4848 StringBuilder buf = new StringBuilder ();
4849 AppendFieldVisibility (buf, field);
4850 if (buf.Length == 0)
4853 buf.Insert (0, ".field ");
4856 buf.Append ("static ");
4857 if (field.IsInitOnly)
4858 buf.Append ("initonly ");
4859 if (field.IsLiteral)
4860 buf.Append ("literal ");
4861 _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
4862 buf.Append (' ').Append (field.Name);
4863 AppendFieldValue (buf, field);
4865 return buf.ToString ();
4868 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4871 return buf.Append ("public ");
4872 if (field.IsFamilyAndAssembly)
4873 return buf.Append ("familyandassembly ");
4874 if (field.IsFamilyOrAssembly)
4875 return buf.Append ("familyorassembly ");
4877 return buf.Append ("family ");
4881 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4883 // enums have a value__ field, which we ignore
4884 if (field.DeclaringType.IsGenericType ())
4886 if (field.HasConstant && field.IsLiteral) {
4889 val = field.Constant;
4894 buf.Append (" = ").Append ("null");
4895 else if (val is Enum)
4897 .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4899 .Append (val.ToString ())
4901 else if (val is IFormattable) {
4902 string value = ((IFormattable)val).ToString();
4905 buf.Append ("\"" + value + "\"");
4907 buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4916 protected override string GetEventDeclaration (EventDefinition e)
4918 StringBuilder buf = new StringBuilder ();
4919 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4924 buf.Append (".event ")
4925 .Append (GetName (e.EventType))
4929 return buf.ToString ();
4933 class ILMemberFormatter : ILFullMemberFormatter {
4934 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4940 class ILNativeTypeMemberFormatter : ILFullMemberFormatter {
4941 protected static string _GetBuiltinType (string t)
4943 //string moddedType = base.GetBuiltinType (t);
4945 //return moddedType;
4949 class CSharpNativeTypeMemberFormatter : CSharpFullMemberFormatter {
4950 protected override string GetCSharpType (string t) {
4951 string moddedType = base.GetCSharpType (t);
4953 switch (moddedType) {
4954 case "int": return "nint";
4959 case "System.Drawing.SizeF":
4960 return "CoreGraphics.CGSize";
4961 case "System.Drawing.PointF":
4962 return "CoreGraphics.CGPoint";
4963 case "System.Drawing.RectangleF":
4964 return "CoreGraphics.CGPoint";
4970 class CSharpFullMemberFormatter : MemberFormatter {
4972 public override string Language {
4976 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4979 string ns = DocUtils.GetNamespace (type);
4980 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
4981 buf.Append (ns).Append ('.');
4985 protected virtual string GetCSharpType (string t)
4988 case "System.Byte": return "byte";
4989 case "System.SByte": return "sbyte";
4990 case "System.Int16": return "short";
4991 case "System.Int32": return "int";
4992 case "System.Int64": return "long";
4994 case "System.UInt16": return "ushort";
4995 case "System.UInt32": return "uint";
4996 case "System.UInt64": return "ulong";
4998 case "System.Single": return "float";
4999 case "System.Double": return "double";
5000 case "System.Decimal": return "decimal";
5001 case "System.Boolean": return "bool";
5002 case "System.Char": return "char";
5003 case "System.Void": return "void";
5004 case "System.String": return "string";
5005 case "System.Object": return "object";
5010 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
5012 if (context != null && context.TransformFlags != null &&
5013 (context.TransformFlags.Count == 0 || context.TransformFlags [context.TransformIndex])) {
5014 context.TransformIndex++;
5015 return buf.Append ("dynamic");
5018 if (type is GenericParameter)
5019 return AppendGenericParameterConstraints (buf, (GenericParameter) type, context).Append (type.Name);
5020 string t = type.FullName;
5021 if (!t.StartsWith ("System.")) {
5022 return base.AppendTypeName (buf, type, context);
5025 string s = GetCSharpType (t);
5027 if (context != null)
5028 context.TransformIndex++;
5029 return buf.Append (s);
5032 return base.AppendTypeName (buf, type, context);
5035 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
5037 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
5039 GenericParameterAttributes attrs = type.Attributes;
5040 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
5041 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
5045 buf.Append ("out ");
5049 protected override string GetTypeDeclaration (TypeDefinition type)
5051 string visibility = GetTypeVisibility (type.Attributes);
5052 if (visibility == null)
5055 StringBuilder buf = new StringBuilder ();
5057 buf.Append (visibility);
5060 MemberFormatter full = new CSharpFullMemberFormatter ();
5062 if (DocUtils.IsDelegate (type)) {
5063 buf.Append("delegate ");
5064 MethodDefinition invoke = type.GetMethod ("Invoke");
5065 buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
5066 buf.Append (GetName (type));
5067 AppendParameters (buf, invoke, invoke.Parameters);
5068 AppendGenericTypeConstraints (buf, type);
5071 return buf.ToString();
5074 if (type.IsAbstract && !type.IsInterface)
5075 buf.Append("abstract ");
5076 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
5077 buf.Append("sealed ");
5078 buf.Replace ("abstract sealed", "static");
5080 buf.Append (GetTypeKind (type));
5082 buf.Append (GetCSharpType (type.FullName) == null
5087 TypeReference basetype = type.BaseType;
5088 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
5091 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
5092 .Select (iface => full.GetName (iface))
5096 if (basetype != null || interface_names.Count > 0)
5099 if (basetype != null) {
5100 buf.Append (full.GetName (basetype));
5101 if (interface_names.Count > 0)
5105 for (int i = 0; i < interface_names.Count; i++){
5108 buf.Append (interface_names [i]);
5110 AppendGenericTypeConstraints (buf, type);
5113 return buf.ToString ();
5116 static string GetTypeKind (TypeDefinition t)
5122 if (t.IsClass || t.FullName == "System.Enum")
5126 throw new ArgumentException(t.FullName);
5129 static string GetTypeVisibility (TypeAttributes ta)
5131 switch (ta & TypeAttributes.VisibilityMask) {
5132 case TypeAttributes.Public:
5133 case TypeAttributes.NestedPublic:
5136 case TypeAttributes.NestedFamily:
5137 case TypeAttributes.NestedFamORAssem:
5145 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
5147 if (type.GenericParameters.Count == 0)
5149 return AppendConstraints (buf, type.GenericParameters);
5152 private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
5154 foreach (GenericParameter genArg in genArgs) {
5155 GenericParameterAttributes attrs = genArg.Attributes;
5156 IList<TypeReference> constraints = genArg.Constraints;
5157 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
5160 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
5161 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
5162 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
5165 if (!isref && !isvt && !isnew && constraints.Count == 0)
5167 buf.Append (" where ").Append (genArg.Name).Append (" : ");
5169 buf.Append ("class");
5173 buf.Append ("struct");
5176 if (constraints.Count > 0 && !isvt) {
5179 buf.Append (GetTypeName (constraints [0]));
5180 for (int i = 1; i < constraints.Count; ++i)
5181 buf.Append (", ").Append (GetTypeName (constraints [i]));
5183 if (isnew && !isvt) {
5186 buf.Append ("new()");
5192 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5194 StringBuilder buf = new StringBuilder ();
5195 AppendVisibility (buf, constructor);
5196 if (buf.Length == 0)
5200 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
5201 AppendParameters (buf, constructor, constructor.Parameters);
5204 return buf.ToString ();
5207 protected override string GetMethodDeclaration (MethodDefinition method)
5209 string decl = base.GetMethodDeclaration (method);
5215 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
5217 if (DocUtils.IsExplicitlyImplemented (method)) {
5218 TypeReference iface;
5219 MethodReference ifaceMethod;
5220 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5221 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
5223 .Append (ifaceMethod.Name);
5225 return base.AppendMethodName (buf, method);
5228 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
5230 if (method.GenericParameters.Count == 0)
5232 return AppendConstraints (buf, method.GenericParameters);
5235 protected override string RefTypeModifier {
5239 protected override string GetFinalizerName (MethodDefinition method)
5241 return "~" + method.DeclaringType.Name + " ()";
5244 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
5248 if (method.IsPublic)
5249 return buf.Append ("public");
5250 if (method.IsFamily || method.IsFamilyOrAssembly)
5251 return buf.Append ("protected");
5255 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
5257 string modifiers = String.Empty;
5258 if (method.IsStatic) modifiers += " static";
5259 if (method.IsVirtual && !method.IsAbstract) {
5260 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
5261 else modifiers += " override";
5263 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
5264 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
5265 if (method.IsFinal) modifiers += " sealed";
5266 if (modifiers == " virtual sealed") modifiers = "";
5268 return buf.Append (modifiers);
5271 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
5273 if (method.IsGenericMethod ()) {
5274 IList<GenericParameter> args = method.GenericParameters;
5275 if (args.Count > 0) {
5277 buf.Append (args [0].Name);
5278 for (int i = 1; i < args.Count; ++i)
5279 buf.Append (",").Append (args [i].Name);
5286 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
5288 return AppendParameters (buf, method, parameters, '(', ')');
5291 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
5295 if (parameters.Count > 0) {
5296 if (DocUtils.IsExtensionMethod (method))
5297 buf.Append ("this ");
5298 AppendParameter (buf, parameters [0]);
5299 for (int i = 1; i < parameters.Count; ++i) {
5301 AppendParameter (buf, parameters [i]);
5305 return buf.Append (end);
5308 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
5310 if (parameter.ParameterType is ByReferenceType) {
5311 if (parameter.IsOut)
5312 buf.Append ("out ");
5314 buf.Append ("ref ");
5316 buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
5317 buf.Append (parameter.Name);
5318 if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant) {
5319 buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
5324 protected override string GetPropertyDeclaration (PropertyDefinition property)
5326 MethodDefinition method;
5328 string get_visible = null;
5329 if ((method = property.GetMethod) != null &&
5330 (DocUtils.IsExplicitlyImplemented (method) ||
5331 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5332 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5333 string set_visible = null;
5334 if ((method = property.SetMethod) != null &&
5335 (DocUtils.IsExplicitlyImplemented (method) ||
5336 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5337 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5339 if ((set_visible == null) && (get_visible == null))
5343 StringBuilder buf = new StringBuilder ();
5344 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
5345 buf.Append (visibility = get_visible);
5346 else if (set_visible != null && get_visible == null)
5347 buf.Append (visibility = set_visible);
5349 buf.Append (visibility = "public");
5351 // Pick an accessor to use for static/virtual/override/etc. checks.
5352 method = property.SetMethod;
5354 method = property.GetMethod;
5356 string modifiers = String.Empty;
5357 if (method.IsStatic) modifiers += " static";
5358 if (method.IsVirtual && !method.IsAbstract) {
5359 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
5360 modifiers += " virtual";
5362 modifiers += " override";
5364 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
5365 if (method.IsAbstract && !declDef.IsInterface)
5366 modifiers += " abstract";
5368 modifiers += " sealed";
5369 if (modifiers == " virtual sealed")
5371 buf.Append (modifiers).Append (' ');
5373 buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
5375 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
5376 string name = property.Name;
5377 foreach (MemberReference mi in defs) {
5378 if (mi == property) {
5383 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
5385 if (property.Parameters.Count != 0) {
5386 AppendParameters (buf, method, property.Parameters, '[', ']');
5390 if (get_visible != null) {
5391 if (get_visible != visibility)
5392 buf.Append (' ').Append (get_visible);
5393 buf.Append (" get;");
5395 if (set_visible != null) {
5396 if (set_visible != visibility)
5397 buf.Append (' ').Append (set_visible);
5398 buf.Append (" set;");
5402 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
5405 protected override string GetFieldDeclaration (FieldDefinition field)
5407 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
5408 if (declType.IsEnum && field.Name == "value__")
5409 return null; // This member of enums aren't documented.
5411 StringBuilder buf = new StringBuilder ();
5412 AppendFieldVisibility (buf, field);
5413 if (buf.Length == 0)
5416 if (declType.IsEnum)
5419 if (field.IsStatic && !field.IsLiteral)
5420 buf.Append (" static");
5421 if (field.IsInitOnly)
5422 buf.Append (" readonly");
5423 if (field.IsLiteral)
5424 buf.Append (" const");
5426 buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
5427 buf.Append (field.Name);
5428 AppendFieldValue (buf, field);
5431 return buf.ToString ();
5434 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
5437 return buf.Append ("public");
5438 if (field.IsFamily || field.IsFamilyOrAssembly)
5439 return buf.Append ("protected");
5443 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
5445 // enums have a value__ field, which we ignore
5446 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
5447 field.DeclaringType.IsGenericType ())
5449 if (field.HasConstant && field.IsLiteral) {
5452 val = field.Constant;
5457 buf.Append (" = ").Append ("null");
5458 else if (val is Enum)
5459 buf.Append (" = ").Append (val.ToString ());
5460 else if (val is IFormattable) {
5461 string value = ((IFormattable)val).ToString();
5463 value = "\"" + value + "\"";
5464 buf.Append (" = ").Append (value);
5470 protected override string GetEventDeclaration (EventDefinition e)
5472 StringBuilder buf = new StringBuilder ();
5473 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
5477 AppendModifiers (buf, e.AddMethod);
5479 buf.Append (" event ");
5480 buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters [0]))).Append (' ');
5481 buf.Append (e.Name).Append (';');
5483 return buf.ToString ();
5487 class CSharpMemberFormatter : CSharpFullMemberFormatter {
5488 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5494 class DocTypeFullMemberFormatter : MemberFormatter {
5495 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
5497 protected override char NestedTypeSeparator {
5502 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
5503 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5509 class SlashDocMemberFormatter : MemberFormatter {
5511 protected override char[] GenericTypeContainer {
5512 get {return new char[]{'{', '}'};}
5515 private bool AddTypeCount = true;
5517 private TypeReference genDeclType;
5518 private MethodReference genDeclMethod;
5520 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
5522 if (type is GenericParameter) {
5524 if (genDeclType != null) {
5525 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
5526 for (int i = 0; i < genArgs.Count; ++i) {
5527 if (genArgs [i].Name == type.Name) {
5528 buf.Append ('`').Append (i);
5533 if (genDeclMethod != null) {
5534 IList<GenericParameter> genArgs = null;
5535 if (genDeclMethod.IsGenericMethod ()) {
5536 genArgs = genDeclMethod.GenericParameters;
5537 for (int i = 0; i < genArgs.Count; ++i) {
5538 if (genArgs [i].Name == type.Name) {
5539 buf.Append ("``").Append (i);
5545 if (genDeclType == null && genDeclMethod == null) {
5546 // Probably from within an explicitly implemented interface member,
5547 // where CSC uses parameter names instead of indices (why?), e.g.
5548 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
5549 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
5550 buf.Append (type.Name);
5552 if (buf.Length == l) {
5553 throw new Exception (string.Format (
5554 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
5555 type.Name, genDeclType, genDeclMethod));
5559 base.AppendTypeName (buf, type, context);
5561 int numArgs = type.GenericParameters.Count;
5562 if (type.DeclaringType != null)
5563 numArgs -= type.GenericParameters.Count;
5565 buf.Append ('`').Append (numArgs);
5572 protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
5574 buf.Append (ArrayDelimeters [0]);
5575 int rank = array.Rank;
5578 for (int i = 1; i < rank; ++i) {
5582 return buf.Append (ArrayDelimeters [1]);
5585 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5588 base.AppendGenericType (buf, type, context);
5590 AppendType (buf, type, context);
5594 private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5596 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
5597 bool insertNested = false;
5598 int prevParamCount = 0;
5599 foreach (var decl in decls) {
5601 buf.Append (NestedTypeSeparator);
5602 insertNested = true;
5603 base.AppendTypeName (buf, decl, context);
5604 int argCount = DocUtils.GetGenericArgumentCount (decl);
5605 int numArgs = argCount - prevParamCount;
5606 prevParamCount = argCount;
5608 buf.Append ('`').Append (numArgs);
5613 public override string GetDeclaration (MemberReference member)
5615 TypeReference r = member as TypeReference;
5617 return "T:" + GetTypeName (r);
5619 return base.GetDeclaration (member);
5622 protected override string GetConstructorName (MethodReference constructor)
5624 return GetMethodDefinitionName (constructor, "#ctor");
5627 protected override string GetMethodName (MethodReference method)
5630 MethodDefinition methodDef = method as MethodDefinition;
5631 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
5634 TypeReference iface;
5635 MethodReference ifaceMethod;
5636 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
5637 AddTypeCount = false;
5638 name = GetTypeName (iface) + "." + ifaceMethod.Name;
5639 AddTypeCount = true;
5641 return GetMethodDefinitionName (method, name);
5644 private string GetMethodDefinitionName (MethodReference method, string name)
5646 StringBuilder buf = new StringBuilder ();
5647 buf.Append (GetTypeName (method.DeclaringType));
5649 buf.Append (name.Replace (".", "#"));
5650 if (method.IsGenericMethod ()) {
5651 IList<GenericParameter> genArgs = method.GenericParameters;
5652 if (genArgs.Count > 0)
5653 buf.Append ("``").Append (genArgs.Count);
5655 IList<ParameterDefinition> parameters = method.Parameters;
5657 genDeclType = method.DeclaringType;
5658 genDeclMethod = method;
5659 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
5663 genDeclMethod = null;
5665 return buf.ToString ();
5668 private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
5670 if (parameters.Count == 0)
5675 AppendParameter (buf, genArgs, parameters [0]);
5676 for (int i = 1; i < parameters.Count; ++i) {
5678 AppendParameter (buf, genArgs, parameters [i]);
5681 return buf.Append (')');
5684 private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
5686 AddTypeCount = false;
5687 buf.Append (GetTypeName (parameter.ParameterType));
5688 AddTypeCount = true;
5692 protected override string GetPropertyName (PropertyReference property)
5696 PropertyDefinition propertyDef = property as PropertyDefinition;
5697 MethodDefinition method = null;
5698 if (propertyDef != null)
5699 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
5700 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
5701 name = property.Name;
5703 TypeReference iface;
5704 MethodReference ifaceMethod;
5705 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5706 AddTypeCount = false;
5707 name = string.Join ("#", new string[]{
5708 GetTypeName (iface).Replace (".", "#"),
5709 DocUtils.GetMember (property.Name)
5711 AddTypeCount = true;
5714 StringBuilder buf = new StringBuilder ();
5715 buf.Append (GetName (property.DeclaringType));
5718 IList<ParameterDefinition> parameters = property.Parameters;
5719 if (parameters.Count > 0) {
5720 genDeclType = property.DeclaringType;
5722 IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
5723 AppendParameter (buf, genArgs, parameters [0]);
5724 for (int i = 1; i < parameters.Count; ++i) {
5726 AppendParameter (buf, genArgs, parameters [i]);
5731 return buf.ToString ();
5734 protected override string GetFieldName (FieldReference field)
5736 return string.Format ("{0}.{1}",
5737 GetName (field.DeclaringType), field.Name);
5740 protected override string GetEventName (EventReference e)
5742 return string.Format ("{0}.{1}",
5743 GetName (e.DeclaringType), e.Name);
5746 protected override string GetTypeDeclaration (TypeDefinition type)
5748 string name = GetName (type);
5754 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5756 string name = GetName (constructor);
5762 protected override string GetMethodDeclaration (MethodDefinition method)
5764 string name = GetName (method);
5767 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
5768 genDeclType = method.DeclaringType;
5769 genDeclMethod = method;
5770 name += "~" + GetName (method.ReturnType);
5772 genDeclMethod = null;
5777 protected override string GetPropertyDeclaration (PropertyDefinition property)
5779 string name = GetName (property);
5785 protected override string GetFieldDeclaration (FieldDefinition field)
5787 string name = GetName (field);
5793 protected override string GetEventDeclaration (EventDefinition e)
5795 string name = GetName (e);
5802 class FileNameMemberFormatter : SlashDocMemberFormatter {
5803 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5808 protected override char NestedTypeSeparator {
5813 class ResolvedTypeInfo {
5814 TypeDefinition typeDef;
5816 public ResolvedTypeInfo (TypeReference value) {
5820 public TypeReference Reference { get; private set; }
5822 public TypeDefinition Definition {
5824 if (typeDef == null) {
5825 typeDef = Reference.Resolve ();
5832 /// <summary>Formats attribute values. Should return true if it is able to format the value.</summary>
5833 class AttributeValueFormatter {
5834 public virtual bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5836 TypeReference valueType = type.Reference;
5838 returnvalue = "null";
5841 if (valueType.FullName == "System.Type") {
5842 var vTypeRef = v as TypeReference;
5843 if (vTypeRef != null)
5844 returnvalue = "typeof(" + NativeTypeManager.GetTranslatedName (vTypeRef) + ")"; // TODO: drop NS handling
5846 returnvalue = "typeof(" + v.ToString () + ")";
5850 if (valueType.FullName == "System.String") {
5851 returnvalue = "\"" + v.ToString () + "\"";
5854 if (valueType.FullName == "System.Char") {
5855 returnvalue = "'" + v.ToString () + "'";
5859 returnvalue = (bool)v ? "true" : "false";
5863 TypeDefinition valueDef = type.Definition;
5864 if (valueDef == null || !valueDef.IsEnum) {
5865 returnvalue = v.ToString ();
5869 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5870 var values = MDocUpdater.GetEnumerationValues (valueDef);
5871 long c = MDocUpdater.ToInt64 (v);
5872 if (values.ContainsKey (c)) {
5873 returnvalue = typename + "." + values [c];
5882 /// <summary>The final value formatter in the pipeline ... if no other formatter formats the value,
5883 /// then this one will serve as the default implementation.</summary>
5884 class DefaultAttributeValueFormatter : AttributeValueFormatter {
5885 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5887 returnvalue = "(" + MDocUpdater.GetDocTypeFullName (type.Reference) + ") " + v.ToString ();
5892 /// <summary>Flags enum formatter that assumes powers of two values.</summary>
5893 /// <remarks>As described here: https://msdn.microsoft.com/en-us/library/vstudio/ms229062(v=vs.100).aspx</remarks>
5894 class StandardFlagsEnumFormatter : AttributeValueFormatter {
5895 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5897 TypeReference valueType = type.Reference;
5898 TypeDefinition valueDef = type.Definition;
5899 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
5901 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5902 var values = MDocUpdater.GetEnumerationValues (valueDef);
5903 long c = MDocUpdater.ToInt64 (v);
5904 returnvalue = string.Join (" | ",
5905 (from i in values.Keys
5906 where (c & i) == i && i != 0
5907 select typename + "." + values [i])
5908 .DefaultIfEmpty (c.ToString ()).ToArray ());
5918 /// <summary>A custom formatter for the ObjCRuntime.Platform enumeration.</summary>
5919 class ApplePlatformEnumFormatter : AttributeValueFormatter {
5920 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5922 TypeReference valueType = type.Reference;
5923 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5924 TypeDefinition valueDef = type.Definition;
5925 if (typename.Contains ("ObjCRuntime.Platform") && valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
5927 var values = MDocUpdater.GetEnumerationValues (valueDef);
5928 long c = MDocUpdater.ToInt64 (v);
5930 returnvalue = Format (c, values, typename);
5938 string Format (long c, IDictionary<long, string> values, string typename)
5940 int iosarch, iosmajor, iosminor, iossubminor;
5941 int macarch, macmajor, macminor, macsubminor;
5942 GetEncodingiOS (c, out iosarch, out iosmajor, out iosminor, out iossubminor);
5943 GetEncodingMac ((ulong)c, out macarch, out macmajor, out macminor, out macsubminor);
5945 if (iosmajor == 0 & iosminor == 0 && iossubminor == 0) {
5946 return FormatValues ("Mac", macarch, macmajor, macminor, macsubminor);
5949 if (macmajor == 0 & macminor == 0 && macsubminor == 0) {
5950 return FormatValues ("iOS", iosarch, iosmajor, iosminor, iossubminor);
5953 return string.Format ("(Platform){0}", c);
5956 string FormatValues (string plat, int arch, int major, int minor, int subminor)
5958 string archstring = "";
5967 return string.Format ("Platform.{4}_{0}_{1}{2} | Platform.{4}_Arch{3}",
5970 subminor == 0 ? "" : "_" + subminor.ToString (),
5976 void GetEncodingiOS (long entireLong, out int archindex, out int major, out int minor, out int subminor)
5978 long lowerBits = entireLong & 0xffffffff;
5979 int lowerBitsAsInt = (int) lowerBits;
5980 GetEncoding (lowerBitsAsInt, out archindex, out major, out minor, out subminor);
5983 void GetEncodingMac (ulong entireLong, out int archindex, out int major, out int minor, out int subminor)
5985 ulong higherBits = entireLong & 0xffffffff00000000;
5986 int higherBitsAsInt = (int) ((higherBits) >> 32);
5987 GetEncoding (higherBitsAsInt, out archindex, out major, out minor, out subminor);
5990 void GetEncoding (Int32 encodedBits, out int archindex, out int major, out int minor, out int subminor)
5992 // format is AAJJNNSS
5993 archindex = (int)((encodedBits & 0xFF000000) >> 24);
5994 major = (int)((encodedBits & 0x00FF0000) >> 16);
5995 minor = (int)((encodedBits & 0x0000FF00) >> 8);
5996 subminor = (int)((encodedBits & 0x000000FF) >> 0);