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 var assemblyNameNode = doc.SelectSingleNode ("/Type/AssemblyInfo/AssemblyName");
1019 if (assemblyNameNode == null){
1020 Warning ("Did not find /Type/AssemblyInfo/AssemblyName on {0}", typefile.FullName);
1023 string assemblyName = assemblyNameNode.InnerText;
1024 AssemblyDefinition assembly = assemblies.FirstOrDefault (a => a.Name.Name == assemblyName);
1026 Action saveDoc = () => {
1027 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
1028 WriteXml(doc.DocumentElement, writer);
1031 if (e != null && !no_assembly_versions && assembly != null && assemblyName != null && UpdateAssemblyVersions (e, assembly, GetAssemblyVersions(assemblyName), false)) {
1033 goodfiles.Add (relTypeFile);
1037 Action actuallyDelete = () => {
1038 string newname = typefile.FullName + ".remove";
1039 try { System.IO.File.Delete (newname); } catch (Exception) { Warning ("Unable to delete existing file: {0}", newname); }
1040 try { typefile.MoveTo (newname); } catch (Exception) { Warning ("Unable to rename to: {0}", newname); }
1041 Console.WriteLine ("Class no longer present; file renamed: " + Path.Combine (nsdir.Name, typefile.Name));
1044 if (string.IsNullOrWhiteSpace (PreserveTag)) { // only do this if there was not a -preserve
1047 var unifiedAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[@apistyle='unified']");
1048 var classicAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[@apistyle='classic']");
1049 var unifiedMembers = doc.SelectNodes ("//Member[@apistyle='unified']|//Member/AssemblyInfo[@apistyle='unified']");
1050 var classicMembers = doc.SelectNodes ("//Member[@apistyle='classic']|//Member/AssemblyInfo[@apistyle='classic']");
1051 bool isUnifiedRun = HasDroppedAnyNamespace ();
1052 bool isClassicOrNormalRun = !isUnifiedRun;
1054 Action<XmlNode, ApiStyle> removeStyles = (x, style) => {
1055 var styledNodes = doc.SelectNodes("//*[@apistyle='"+ style.ToString ().ToLowerInvariant () +"']");
1056 if (styledNodes != null && styledNodes.Count > 0) {
1057 foreach(var node in styledNodes.Cast<XmlNode> ()) {
1058 node.ParentNode.RemoveChild (node);
1063 if (isClassicOrNormalRun) {
1064 if (unifiedAssemblyNode != null || unifiedMembers.Count > 0) {
1065 Warning ("*** this type is marked as unified, not deleting during this run: {0}", typefile.FullName);
1066 // if truly removed from both assemblies, it will be removed fully during the unified run
1067 removeStyles (doc, ApiStyle.Classic);
1070 // we should be safe to delete here because it was not marked as a unified assembly
1075 if (classicAssemblyNode != null || classicMembers.Count > 0) {
1076 Warning ("*** this type is marked as classic, not deleting {0}", typefile.FullName);
1079 // safe to delete because it wasn't marked as a classic assembly, so the type is gone in both.
1089 private static TextWriter OpenWrite (string path, FileMode mode)
1091 var w = new StreamWriter (
1092 new FileStream (path, mode),
1093 new UTF8Encoding (false)
1099 private string[] GetAssemblyVersions (string assemblyName)
1101 return (from a in assemblies
1102 where a.Name.Name == assemblyName
1103 select GetAssemblyVersion (a)).ToArray ();
1106 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
1108 // Look for type nodes that no longer correspond to types
1109 MyXmlNodeList remove = new MyXmlNodeList ();
1110 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
1111 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
1112 if (!goodfiles.Contains (fulltypename)) {
1113 remove.Add (typenode);
1116 foreach (XmlNode n in remove)
1117 n.ParentNode.RemoveChild (n);
1120 private void CleanupExtensions (XmlElement index_types)
1122 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
1123 if (extensionMethods.Count == 0) {
1126 index_types.SelectSingleNode ("/Overview").RemoveChild (e);
1130 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
1131 index_types.SelectSingleNode ("/Overview").AppendChild (e);
1135 extensionMethods.Sort (DefaultExtensionMethodComparer);
1136 foreach (XmlNode m in extensionMethods) {
1137 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
1141 class ExtensionMethodComparer : XmlNodeComparer {
1142 public override int Compare (XmlNode x, XmlNode y)
1144 XmlNode xLink = x.SelectSingleNode ("Member/Link");
1145 XmlNode yLink = y.SelectSingleNode ("Member/Link");
1147 int n = xLink.Attributes ["Type"].Value.CompareTo (
1148 yLink.Attributes ["Type"].Value);
1151 n = xLink.Attributes ["Member"].Value.CompareTo (
1152 yLink.Attributes ["Member"].Value);
1153 if (n == 0 && !object.ReferenceEquals (x, y))
1154 throw new InvalidOperationException ("Duplicate extension method found!");
1159 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
1161 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince)
1163 Console.WriteLine(message + ": " + type.FullName);
1165 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
1167 // Update type metadata
1168 UpdateType(basefile.DocumentElement, type);
1170 // Update existing members. Delete member nodes that no longer should be there,
1171 // and remember what members are already documented so we don't add them again.
1173 MyXmlNodeList todelete = new MyXmlNodeList ();
1175 foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
1176 XmlElement oldmember = info.Node;
1177 MemberReference oldmember2 = info.Member;
1179 if (info.Member != null && info.Node != null) {
1180 // Check for an error condition where the xml MemberName doesn't match the matched member
1181 var memberName = GetMemberName (info.Member);
1182 var memberAttribute = info.Node.Attributes ["MemberName"];
1183 if (memberAttribute == null || (memberAttribute.Value != memberName && memberAttribute.Value.Split (',').Length != memberName.Split (',').Length)) {
1184 oldmember.SetAttribute ("MemberName", memberName);
1188 string sig = oldmember2 != null ? memberFormatters [0].GetDeclaration (oldmember2) : null;
1190 // Interface implementations and overrides are deleted from the docs
1191 // unless the overrides option is given.
1192 if (oldmember2 != null && sig == null)
1195 // Deleted (or signature changed)
1196 if (oldmember2 == null) {
1197 if (!no_assembly_versions && UpdateAssemblyVersions (oldmember, type.Module.Assembly, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
1200 DeleteMember ("Member Removed", output, oldmember, todelete, type);
1205 if (seenmembers.ContainsKey (sig)) {
1206 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
1207 // ignore, already seen
1209 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
1210 DeleteMember ("Duplicate Member Found", output, oldmember, todelete, type);
1212 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
1216 // Update signature information
1219 // get all apistyles of sig from info.Node
1220 var styles = oldmember.GetElementsByTagName ("MemberSignature").Cast<XmlElement> ()
1221 .Where (x => x.GetAttribute ("Language") == "C#" && !seenmembers.ContainsKey(x.GetAttribute("Value")))
1222 .Select (x => x.GetAttribute ("Value"));
1224 foreach (var stylesig in styles) {
1225 seenmembers.Add (stylesig, oldmember);
1228 foreach (XmlElement oldmember in todelete)
1229 oldmember.ParentNode.RemoveChild (oldmember);
1232 if (!DocUtils.IsDelegate (type)) {
1233 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
1234 var typemembers = type.GetMembers()
1236 if (m is TypeDefinition) return false;
1237 string sig = memberFormatters [0].GetDeclaration (m);
1238 if (sig == null) return false;
1239 if (seenmembers.ContainsKey(sig)) return false;
1241 // Verify that the member isn't an explicitly implemented
1242 // member of an internal interface, in which case we shouldn't return true.
1243 MethodDefinition methdef = null;
1244 if (m is MethodDefinition)
1245 methdef = m as MethodDefinition;
1246 else if (m is PropertyDefinition) {
1247 var prop = m as PropertyDefinition;
1248 methdef = prop.GetMethod ?? prop.SetMethod;
1251 if (methdef != null) {
1252 TypeReference iface;
1253 MethodReference imethod;
1255 if (methdef.Overrides.Count == 1) {
1256 DocUtils.GetInfoForExplicitlyImplementedMethod (methdef, out iface, out imethod);
1257 if (!IsPublic (iface.Resolve ())) return false;
1264 foreach (MemberReference m in typemembers) {
1265 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
1266 if (mm == null) continue;
1268 if (MDocUpdater.SwitchingToMagicTypes) {
1269 // this is a unified style API that obviously doesn't exist in the classic API. Let's mark
1270 // it with apistyle="unified", so that it's not displayed for classic style APIs
1271 mm.SetAttribute ("apistyle", "unified");
1274 members.AppendChild( mm );
1276 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
1281 // Import code snippets from files
1282 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
1283 if (!(code is XmlElement)) continue;
1284 string file = ((XmlElement)code).GetAttribute("src");
1285 string lang = ((XmlElement)code).GetAttribute("lang");
1287 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
1289 code.InnerText = src;
1293 if (insertSince && since != null) {
1294 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
1295 docs.AppendChild (CreateSinceNode (basefile));
1299 XmlElement d = basefile.DocumentElement ["Docs"];
1300 XmlElement m = basefile.DocumentElement ["Members"];
1301 if (d != null && m != null)
1302 basefile.DocumentElement.InsertBefore (
1303 basefile.DocumentElement.RemoveChild (d), m);
1304 SortTypeMembers (m);
1308 WriteXml(basefile.DocumentElement, Console.Out);
1310 FileInfo file = new FileInfo (output);
1311 if (!file.Directory.Exists) {
1312 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
1313 file.Directory.Create ();
1315 WriteFile (output, FileMode.Create,
1316 writer => WriteXml(basefile.DocumentElement, writer));
1320 private string GetCodeSource (string lang, string file)
1323 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
1324 // Grab the specified region
1325 string region = "#region " + file.Substring (anchorStart + 4);
1326 file = file.Substring (0, anchorStart + 3);
1328 using (StreamReader reader = new StreamReader (file)) {
1330 StringBuilder src = new StringBuilder ();
1332 while ((line = reader.ReadLine ()) != null) {
1333 if (line.Trim() == region) {
1334 indent = line.IndexOf (region);
1337 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
1342 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
1345 return src.ToString ();
1347 } catch (Exception e) {
1348 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
1349 file, region, show_exceptions ? e.ToString () : e.Message);
1354 using (StreamReader reader = new StreamReader (file))
1355 return reader.ReadToEnd ();
1356 } catch (Exception e) {
1357 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
1362 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete, TypeDefinition type)
1364 string format = output != null
1365 ? "{0}: File='{1}'; Signature='{4}'"
1366 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1367 string signature = member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value;
1371 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1372 member.Attributes ["MemberName"].Value,
1375 // Identify all of the different states that could affect our decision to delete the member
1376 bool shouldPreserve = !string.IsNullOrWhiteSpace (PreserveTag);
1377 bool hasContent = MemberDocsHaveUserContent (member);
1378 bool shouldDelete = !shouldPreserve && (delete || !hasContent);
1380 bool unifiedRun = HasDroppedNamespace (type);
1382 var classicAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[@apistyle='classic']");
1383 bool nodeIsClassic = classicAssemblyInfo != null || member.HasApiStyle (ApiStyle.Classic);
1384 var unifiedAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']");
1385 bool nodeIsUnified = unifiedAssemblyInfo != null || member.HasApiStyle (ApiStyle.Unified);
1387 Action actuallyDelete = () => {
1388 todelete.Add (member);
1392 if (!shouldDelete) {
1393 // explicitly not deleting
1394 string message = shouldPreserve ?
1395 "Not deleting '{0}' due to --preserve." :
1396 "Not deleting '{0}'; must be enabled with the --delete option";
1397 Warning (message, signature);
1398 } else if (unifiedRun && nodeIsClassic) {
1399 // this is a unified run, and the member doesn't exist, but is marked as being in the classic assembly.
1400 member.RemoveApiStyle (ApiStyle.Unified);
1401 Warning ("Not removing '{0}' since it's still in the classic assembly.", signature);
1402 } else if (unifiedRun && !nodeIsClassic) {
1403 // unified run, and the node is not classic, which means it doesn't exist anywhere.
1406 if (!nodeIsClassic && !nodeIsUnified) { // regular codepath (ie. not classic/unified)
1408 } else { // this is a classic run
1409 Warning ("Removing classic from '{0}' ... will be removed in the unified run if not present there.", signature);
1410 member.RemoveApiStyle (ApiStyle.Classic);
1411 if (classicAssemblyInfo != null) {
1412 member.RemoveChild (classicAssemblyInfo);
1418 class MemberComparer : XmlNodeComparer {
1419 public override int Compare (XmlNode x, XmlNode y)
1422 string xMemberName = x.Attributes ["MemberName"].Value;
1423 string yMemberName = y.Attributes ["MemberName"].Value;
1425 // generic methods *end* with '>'
1426 // it's possible for explicitly implemented generic interfaces to
1427 // contain <...> without being a generic method
1428 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1429 (r = xMemberName.CompareTo (yMemberName)) != 0)
1433 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1434 xMemberName = xMemberName.Substring (0, lt);
1435 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1436 yMemberName = yMemberName.Substring (0, lt);
1437 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1440 // if @MemberName matches, then it's either two different types of
1441 // members sharing the same name, e.g. field & property, or it's an
1442 // overloaded method.
1443 // for different type, sort based on MemberType value.
1444 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1445 y.SelectSingleNode ("MemberType").InnerText);
1449 // same type -- must be an overloaded method. Sort based on type
1450 // parameter count, then parameter count, then by the parameter
1452 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1453 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1454 if (xTypeParams.Count != yTypeParams.Count)
1455 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1456 for (int i = 0; i < xTypeParams.Count; ++i) {
1457 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1458 yTypeParams [i].Attributes ["Name"].Value);
1463 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1464 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1465 if (xParams.Count != yParams.Count)
1466 return xParams.Count <= yParams.Count ? -1 : 1;
1467 for (int i = 0; i < xParams.Count; ++i) {
1468 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1469 yParams [i].Attributes ["Type"].Value);
1473 // all parameters match, but return value might not match if it was
1474 // changed between one version and another.
1475 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1476 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1477 if (xReturn != null && yReturn != null) {
1478 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1487 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1489 private static void SortTypeMembers (XmlNode members)
1491 if (members == null)
1493 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1496 private static bool MemberDocsHaveUserContent (XmlNode e)
1498 e = (XmlElement)e.SelectSingleNode("Docs");
1499 if (e == null) return false;
1500 foreach (XmlElement d in e.SelectNodes("*"))
1501 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1506 // UPDATE HELPER FUNCTIONS
1508 // CREATE A STUB DOCUMENTATION FILE
1510 public XmlElement StubType (TypeDefinition type, string output)
1512 string typesig = typeFormatters [0].GetDeclaration (type);
1513 if (typesig == null) return null; // not publicly visible
1515 XmlDocument doc = new XmlDocument();
1516 XmlElement root = doc.CreateElement("Type");
1517 doc.AppendChild (root);
1519 DoUpdateType2 ("New Type", doc, type, output, true);
1524 private XmlElement CreateSinceNode (XmlDocument doc)
1526 XmlElement s = doc.CreateElement ("since");
1527 s.SetAttribute ("version", since);
1531 // STUBBING/UPDATING FUNCTIONS
1533 public void UpdateType (XmlElement root, TypeDefinition type)
1535 root.SetAttribute("Name", GetDocTypeName (type));
1536 root.SetAttribute("FullName", GetDocTypeFullName (type));
1538 foreach (MemberFormatter f in typeFormatters) {
1539 string element = "TypeSignature[@Language='" + f.Language + "']";
1540 string valueToUse = f.GetDeclaration (type);
1543 root.SelectNodes (element).Cast<XmlElement> ().ToArray (),
1544 x => x.GetAttribute ("Value") == valueToUse,
1545 x => x.SetAttribute ("Value", valueToUse),
1547 var node = WriteElementAttribute (root, element, "Language", f.Language, forceNewElement: true);
1548 var newnode = WriteElementAttribute (root, node, "Value", valueToUse);
1554 AddAssemblyNameToNode (root, type);
1556 string assemblyInfoNodeFilter = MDocUpdater.HasDroppedNamespace (type) ? "[@apistyle='unified']" : "[not(@apistyle) or @apistyle='classic']";
1557 Func<XmlElement, bool> assemblyFilter = x => x.SelectSingleNode ("AssemblyName").InnerText == type.Module.Assembly.Name.Name;
1558 foreach(var ass in root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ().Where (assemblyFilter))
1560 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1561 if (!no_assembly_versions) {
1562 UpdateAssemblyVersions (ass, type, true);
1565 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1566 foreach (var version in versions)
1567 ass.RemoveChild (version);
1569 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1570 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1572 ClearElement(ass, "AssemblyCulture");
1575 // Why-oh-why do we put assembly attributes in each type file?
1576 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1577 // since they're outdated in current docs, and a waste of space.
1578 //MakeAttributes(ass, type.Assembly, true);
1579 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1580 if (assattrs != null)
1581 ass.RemoveChild(assattrs);
1583 NormalizeWhitespace(ass);
1586 if (type.IsGenericType ()) {
1587 MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
1589 ClearElement(root, "TypeParameters");
1592 if (type.BaseType != null) {
1593 XmlElement basenode = WriteElement(root, "Base");
1595 string basetypename = GetDocTypeFullName (type.BaseType);
1596 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1597 WriteElementText(root, "Base/BaseTypeName", basetypename);
1599 // Document how this type instantiates the generic parameters of its base type
1600 TypeReference origBase = type.BaseType.GetElementType ();
1601 if (origBase.IsGenericType ()) {
1602 ClearElement(basenode, "BaseTypeArguments");
1603 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1604 IList<TypeReference> baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1605 IList<GenericParameter> baseGenParams = origBase.GenericParameters;
1606 if (baseGenArgs.Count != baseGenParams.Count)
1607 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1608 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1609 GenericParameter param = baseGenParams [i];
1610 TypeReference value = baseGenArgs [i];
1612 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1613 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1614 bta.AppendChild(arg);
1615 arg.SetAttribute ("TypeParamName", param.Name);
1616 arg.InnerText = GetDocTypeFullName (value);
1620 ClearElement(root, "Base");
1623 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1624 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1625 List<string> interface_names = userInterfaces
1626 .Select (iface => GetDocTypeFullName (iface))
1630 XmlElement interfaces = WriteElement(root, "Interfaces");
1631 interfaces.RemoveAll();
1632 foreach (string iname in interface_names) {
1633 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1634 interfaces.AppendChild(iface);
1635 WriteElementText(iface, "InterfaceName", iname);
1638 ClearElement(root, "Interfaces");
1641 MakeAttributes (root, GetCustomAttributes (type), type);
1643 if (DocUtils.IsDelegate (type)) {
1644 MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
1645 var member = type.GetMethod ("Invoke");
1646 MakeParameters(root, member, member.Parameters);
1647 MakeReturnValue(root, member);
1650 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1651 MakeDocNode (typeInfo);
1653 if (!DocUtils.IsDelegate (type))
1654 WriteElement (root, "Members");
1656 OrderTypeNodes (root, root.ChildNodes);
1657 NormalizeWhitespace(root);
1660 /// <summary>Adds an AssemblyInfo with AssemblyName node to an XmlElement.</summary>
1661 /// <returns>The assembly that was either added, or was already present</returns>
1662 static XmlElement AddAssemblyNameToNode (XmlElement root, TypeDefinition type)
1664 return AddAssemblyNameToNode (root, type.Module);
1667 /// <summary>Adds an AssemblyInfo with AssemblyName node to an XmlElement.</summary>
1668 /// <returns>The assembly that was either added, or was already present</returns>
1669 static XmlElement AddAssemblyNameToNode (XmlElement root, ModuleDefinition module)
1671 Func<XmlElement, bool> assemblyFilter = x => {
1672 var existingName = x.SelectSingleNode ("AssemblyName");
1673 return existingName != null && existingName.InnerText == module.Assembly.Name.Name;
1676 return AddAssemblyXmlNode (
1677 root.SelectNodes ("AssemblyInfo").Cast<XmlElement> ().ToArray (),
1678 assemblyFilter, x => WriteElementText (x, "AssemblyName", module.Assembly.Name.Name),
1680 XmlElement ass = WriteElement (root, "AssemblyInfo", forceNewElement: true);
1681 if (MDocUpdater.HasDroppedNamespace (module))
1682 ass.SetAttribute ("apistyle", "unified");
1687 static readonly string[] TypeNodeOrder = {
1691 "ThreadingSafetyStatement",
1692 "ThreadSafetyStatement",
1704 static void OrderTypeNodes (XmlNode member, XmlNodeList children)
1706 ReorderNodes (member, children, TypeNodeOrder);
1709 internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1711 List<T> l = new List<T> (list);
1716 private void UpdateMember (DocsNodeInfo info)
1718 XmlElement me = (XmlElement) info.Node;
1719 MemberReference mi = info.Member;
1721 foreach (MemberFormatter f in memberFormatters) {
1722 string element = "MemberSignature[@Language='" + f.Language + "']";
1724 var valueToUse = f.GetDeclaration (mi);
1727 me.SelectNodes (element).Cast<XmlElement> ().ToArray(),
1728 x => x.GetAttribute("Value") == valueToUse,
1729 x => x.SetAttribute ("Value", valueToUse),
1731 var node = WriteElementAttribute (me, element, "Language", f.Language, forceNewElement:true);
1732 var newNode = WriteElementAttribute (me, node, "Value", valueToUse);
1739 WriteElementText(me, "MemberType", GetMemberType(mi));
1741 if (!no_assembly_versions) {
1743 UpdateAssemblyVersions (me, mi, true);
1745 var node = AddAssemblyNameToNode (me, mi.Module);
1747 UpdateAssemblyVersionForAssemblyInfo (node, me, new[] { GetAssemblyVersion (mi.Module.Assembly) }, add: true);
1751 ClearElement (me, "AssemblyInfo");
1754 MakeAttributes (me, GetCustomAttributes (mi), mi.DeclaringType);
1756 MakeReturnValue(me, mi, MDocUpdater.HasDroppedNamespace(mi));
1757 if (mi is MethodReference) {
1758 MethodReference mb = (MethodReference) mi;
1759 if (mb.IsGenericMethod ())
1760 MakeTypeParameters (me, mb.GenericParameters, mi, MDocUpdater.HasDroppedNamespace(mi));
1762 MakeParameters(me, mi, MDocUpdater.HasDroppedNamespace(mi));
1765 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1766 WriteElementText(me, "MemberValue", fieldValue);
1768 info.Node = WriteElement (me, "Docs");
1770 OrderMemberNodes (me, me.ChildNodes);
1771 UpdateExtensionMethods (me, info);
1774 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, MemberReference member) {
1775 AddXmlNode (relevant, valueMatches, setValue, makeNewNode, member.Module);
1778 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, TypeDefinition type) {
1779 AddXmlNode (relevant, valueMatches, setValue, makeNewNode, type.Module);
1782 static XmlElement AddAssemblyXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, ModuleDefinition module)
1784 bool isUnified = MDocUpdater.HasDroppedNamespace (module);
1785 XmlElement thisAssemblyNode = relevant.FirstOrDefault (valueMatches);
1786 if (thisAssemblyNode == null) {
1787 thisAssemblyNode = makeNewNode ();
1788 setValue (thisAssemblyNode);
1792 thisAssemblyNode.AddApiStyle (ApiStyle.Unified);
1794 foreach (var otherNodes in relevant.Where (n => n != thisAssemblyNode && n.DoesNotHaveApiStyle (ApiStyle.Unified))) {
1795 otherNodes.AddApiStyle (ApiStyle.Classic);
1798 return thisAssemblyNode;
1801 /// <summary>Adds an xml node, reusing the node if it's available</summary>
1802 /// <param name="relevant">The existing set of nodes</param>
1803 /// <param name="valueMatches">Checks to see if the node's value matches what you're trying to write.</param>
1804 /// <param name="setValue">Sets the node's value</param>
1805 /// <param name="makeNewNode">Creates a new node, if valueMatches returns false.</param>
1806 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, ModuleDefinition module)
1808 bool shouldDuplicate = MDocUpdater.HasDroppedNamespace (module);
1809 var styleToUse = shouldDuplicate ? ApiStyle.Unified : ApiStyle.Classic;
1810 var existing = relevant;
1812 bool addedOldApiStyle = false;
1814 if (shouldDuplicate) {
1815 existing = existing.Where (n => n.HasApiStyle (styleToUse)).ToArray ();
1816 foreach (var n in relevant.Where (n => n.DoesNotHaveApiStyle (styleToUse))) {
1817 if (valueMatches (n)) {
1821 n.AddApiStyle (ApiStyle.Classic);
1822 addedOldApiStyle = true;
1827 if (!existing.Any ()) {
1828 var newNode = makeNewNode ();
1829 if (shouldDuplicate && addedOldApiStyle) {
1830 newNode.AddApiStyle (ApiStyle.Unified);
1834 var itemToReuse = existing.First ();
1835 setValue (itemToReuse);
1837 if (shouldDuplicate && addedOldApiStyle) {
1838 itemToReuse.AddApiStyle (styleToUse);
1844 static readonly string[] MemberNodeOrder = {
1859 static void OrderMemberNodes (XmlNode member, XmlNodeList children)
1861 ReorderNodes (member, children, MemberNodeOrder);
1864 static void ReorderNodes (XmlNode node, XmlNodeList children, string[] ordering)
1866 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1867 for (int i = 0; i < ordering.Length; ++i) {
1868 for (int j = 0; j < children.Count; ++j) {
1869 XmlNode c = children [j];
1870 if (c.Name == ordering [i]) {
1871 newChildren.Add (c);
1875 if (newChildren.Count >= 0)
1876 node.PrependChild ((XmlNode) newChildren [0]);
1877 for (int i = 1; i < newChildren.Count; ++i) {
1878 XmlNode prev = (XmlNode) newChildren [i-1];
1879 XmlNode cur = (XmlNode) newChildren [i];
1880 node.RemoveChild (cur);
1881 node.InsertAfter (cur, prev);
1885 IEnumerable<string> GetCustomAttributes (MemberReference mi)
1887 IEnumerable<string> attrs = Enumerable.Empty<string>();
1889 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1891 attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1893 PropertyDefinition pd = mi as PropertyDefinition;
1895 if (pd.GetMethod != null)
1896 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1897 if (pd.SetMethod != null)
1898 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1901 EventDefinition ed = mi as EventDefinition;
1903 if (ed.AddMethod != null)
1904 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1905 if (ed.RemoveMethod != null)
1906 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1912 IEnumerable<string> GetCustomAttributes (IList<CustomAttribute> attributes, string prefix)
1914 foreach (CustomAttribute attribute in attributes.OrderBy (ca => ca.AttributeType.FullName)) {
1916 TypeDefinition attrType = attribute.AttributeType as TypeDefinition;
1917 if (attrType != null && !IsPublic (attrType))
1919 if (slashdocFormatter.GetName (attribute.AttributeType) == null)
1922 if (Array.IndexOf (IgnorableAttributes, attribute.AttributeType.FullName) >= 0)
1925 StringList fields = new StringList ();
1927 for (int i = 0; i < attribute.ConstructorArguments.Count; ++i) {
1928 CustomAttributeArgument argument = attribute.ConstructorArguments [i];
1929 fields.Add (MakeAttributesValueString (
1934 (from namedArg in attribute.Fields
1935 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value })
1937 (from namedArg in attribute.Properties
1938 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value }))
1939 .OrderBy (v => v.Name);
1940 foreach (var d in namedArgs)
1941 fields.Add (string.Format ("{0}={1}", d.Name,
1942 MakeAttributesValueString (d.Value, d.Type)));
1944 string a2 = String.Join(", ", fields.ToArray ());
1945 if (a2 != "") a2 = "(" + a2 + ")";
1947 string name = attribute.GetDeclaringType();
1948 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1949 yield return prefix + name + a2;
1953 static readonly string[] ValidExtensionMembers = {
1962 static readonly string[] ValidExtensionDocMembers = {
1968 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1970 MethodDefinition me = info.Member as MethodDefinition;
1973 if (info.Parameters.Count < 1)
1975 if (!DocUtils.IsExtensionMethod (me))
1978 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1979 XmlNode member = e.CloneNode (true);
1980 em.AppendChild (member);
1981 RemoveExcept (member, ValidExtensionMembers);
1982 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1983 WriteElementText (member, "MemberType", "ExtensionMethod");
1984 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1985 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1986 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1987 member.AppendChild (link);
1988 AddTargets (em, info);
1990 extensionMethods.Add (em);
1993 private static void RemoveExcept (XmlNode node, string[] except)
1997 MyXmlNodeList remove = null;
1998 foreach (XmlNode n in node.ChildNodes) {
1999 if (Array.BinarySearch (except, n.Name) < 0) {
2001 remove = new MyXmlNodeList ();
2006 foreach (XmlNode n in remove)
2007 node.RemoveChild (n);
2010 private static void AddTargets (XmlNode member, DocsNodeInfo info)
2012 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
2013 member.PrependChild (targets);
2014 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
2015 AppendElementAttributeText (targets, "Target", "Type",
2016 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
2019 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
2020 IList<TypeReference> constraints = gp.Constraints;
2021 if (constraints.Count == 0)
2022 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
2024 foreach (TypeReference c in constraints)
2025 AppendElementAttributeText(targets, "Target", "Type",
2026 slashdocFormatter.GetDeclaration (c));
2030 private static bool GetFieldConstValue (FieldDefinition field, out string value)
2033 TypeDefinition type = field.DeclaringType.Resolve ();
2034 if (type != null && type.IsEnum) return false;
2036 if (type != null && type.IsGenericType ()) return false;
2037 if (!field.HasConstant)
2039 if (field.IsLiteral) {
2040 object val = field.Constant;
2041 if (val == null) value = "null";
2042 else if (val is Enum) value = val.ToString();
2043 else if (val is IFormattable) {
2044 value = ((IFormattable)val).ToString();
2046 value = "\"" + value + "\"";
2048 if (value != null && value != "")
2054 // XML HELPER FUNCTIONS
2056 internal static XmlElement WriteElement(XmlNode parent, string element, bool forceNewElement = false) {
2057 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
2058 if (ret == null || forceNewElement) {
2059 string[] path = element.Split('/');
2060 foreach (string p in path) {
2061 ret = (XmlElement)parent.SelectSingleNode(p);
2062 if (ret == null || forceNewElement) {
2064 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
2065 ename = ename.Substring(0, ename.IndexOf('['));
2066 ret = parent.OwnerDocument.CreateElement(ename);
2067 parent.AppendChild(ret);
2076 private static XmlElement WriteElementText(XmlNode parent, string element, string value, bool forceNewElement = false) {
2077 XmlElement node = WriteElement(parent, element, forceNewElement: forceNewElement);
2078 node.InnerText = value;
2082 static XmlElement AppendElementText (XmlNode parent, string element, string value)
2084 XmlElement n = parent.OwnerDocument.CreateElement (element);
2085 parent.AppendChild (n);
2086 n.InnerText = value;
2090 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
2092 XmlElement n = parent.OwnerDocument.CreateElement (element);
2093 parent.AppendChild (n);
2094 n.SetAttribute (attribute, value);
2098 internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
2100 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
2101 dest.AppendChild (copy);
2105 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
2106 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
2109 node = WriteElement(parent, element);
2110 node.InnerText = value;
2112 private static XmlElement WriteElementAttribute(XmlElement parent, string element, string attribute, string value, bool forceNewElement = false) {
2113 XmlElement node = WriteElement(parent, element, forceNewElement:forceNewElement);
2114 return WriteElementAttribute (parent, node, attribute, value);
2116 private static XmlElement WriteElementAttribute(XmlElement parent, XmlElement node, string attribute, string value) {
2117 if (node.GetAttribute (attribute) != value) {
2118 node.SetAttribute (attribute, value);
2122 internal static void ClearElement(XmlElement parent, string name) {
2123 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
2125 parent.RemoveChild(node);
2128 // DOCUMENTATION HELPER FUNCTIONS
2130 private void MakeDocNode (DocsNodeInfo info)
2132 List<GenericParameter> genericParams = info.GenericParameters;
2133 IList<ParameterDefinition> parameters = info.Parameters;
2134 TypeReference returntype = info.ReturnType;
2135 bool returnisreturn = info.ReturnIsReturn;
2136 XmlElement e = info.Node;
2137 bool addremarks = info.AddRemarks;
2139 WriteElementInitialText(e, "summary", "To be added.");
2141 if (parameters != null) {
2142 string[] values = new string [parameters.Count];
2143 for (int i = 0; i < values.Length; ++i)
2144 values [i] = parameters [i].Name;
2145 UpdateParameters (e, "param", values);
2148 if (genericParams != null) {
2149 string[] values = new string [genericParams.Count];
2150 for (int i = 0; i < values.Length; ++i)
2151 values [i] = genericParams [i].Name;
2152 UpdateParameters (e, "typeparam", values);
2155 string retnodename = null;
2156 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
2157 retnodename = returnisreturn ? "returns" : "value";
2158 string retnodename_other = !returnisreturn ? "returns" : "value";
2160 // If it has a returns node instead of a value node, change its name.
2161 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
2162 if (retother != null) {
2163 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
2164 foreach (XmlNode node in retother)
2165 retnode.AppendChild(node.CloneNode(true));
2166 e.ReplaceChild(retnode, retother);
2168 WriteElementInitialText(e, retnodename, "To be added.");
2171 ClearElement(e, "returns");
2172 ClearElement(e, "value");
2176 WriteElementInitialText(e, "remarks", "To be added.");
2178 if (exceptions.HasValue && info.Member != null &&
2179 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
2180 UpdateExceptions (e, info.Member);
2183 foreach (DocumentationImporter importer in importers)
2184 importer.ImportDocumentation (info);
2186 OrderDocsNodes (e, e.ChildNodes);
2187 NormalizeWhitespace(e);
2190 static readonly string[] DocsNodeOrder = {
2191 "typeparam", "param", "summary", "returns", "value", "remarks",
2194 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
2196 ReorderNodes (docs, children, DocsNodeOrder);
2200 private void UpdateParameters (XmlElement e, string element, string[] values)
2202 if (values != null) {
2203 XmlNode[] paramnodes = new XmlNode[values.Length];
2205 // Some documentation had param nodes with leading spaces.
2206 foreach (XmlElement paramnode in e.SelectNodes(element)){
2207 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
2210 // If a member has only one parameter, we can track changes to
2211 // the name of the parameter easily.
2212 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
2213 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
2216 bool reinsert = false;
2218 // Pick out existing and still-valid param nodes, and
2219 // create nodes for parameters not in the file.
2220 Hashtable seenParams = new Hashtable();
2221 for (int pi = 0; pi < values.Length; pi++) {
2222 string p = values [pi];
2225 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
2226 if (paramnodes[pi] != null) continue;
2228 XmlElement pe = e.OwnerDocument.CreateElement(element);
2229 pe.SetAttribute("name", p);
2230 pe.InnerText = "To be added.";
2231 paramnodes[pi] = pe;
2235 // Remove parameters that no longer exist and check all params are in the right order.
2237 MyXmlNodeList todelete = new MyXmlNodeList ();
2238 foreach (XmlElement paramnode in e.SelectNodes(element)) {
2239 string name = paramnode.GetAttribute("name");
2240 if (!seenParams.ContainsKey(name)) {
2241 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2242 Warning ("The following param node can only be deleted if the --delete option is given: ");
2243 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
2245 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
2246 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2250 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
2251 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2252 e.ParentNode.Attributes ["MemberName"].Value,
2255 Warning ("\tValue={0}", paramnode.OuterXml);
2257 todelete.Add (paramnode);
2262 if ((int)seenParams[name] != idx)
2268 foreach (XmlNode n in todelete) {
2269 n.ParentNode.RemoveChild (n);
2272 // Re-insert the parameter nodes at the top of the doc section.
2274 for (int pi = values.Length-1; pi >= 0; pi--)
2275 e.PrependChild(paramnodes[pi]);
2277 // Clear all existing param nodes
2278 foreach (XmlNode paramnode in e.SelectNodes(element)) {
2279 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2280 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
2281 Console.WriteLine(paramnode.OuterXml);
2283 paramnode.ParentNode.RemoveChild(paramnode);
2289 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
2291 string existingName = pe.GetAttribute ("name");
2292 pe.SetAttribute ("name", newName);
2293 if (existingName == newName)
2295 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
2296 if (paramref.GetAttribute ("name").Trim () == existingName)
2297 paramref.SetAttribute ("name", newName);
2300 class CrefComparer : XmlNodeComparer {
2302 public CrefComparer ()
2306 public override int Compare (XmlNode x, XmlNode y)
2308 string xType = x.Attributes ["cref"].Value;
2309 string yType = y.Attributes ["cref"].Value;
2310 string xNamespace = GetNamespace (xType);
2311 string yNamespace = GetNamespace (yType);
2313 int c = xNamespace.CompareTo (yNamespace);
2316 return xType.CompareTo (yType);
2319 static string GetNamespace (string type)
2321 int n = type.LastIndexOf ('.');
2323 return type.Substring (0, n);
2324 return string.Empty;
2328 private void UpdateExceptions (XmlNode docs, MemberReference member)
2330 string indent = new string (' ', 10);
2331 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
2332 string cref = slashdocFormatter.GetDeclaration (source.Exception);
2333 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
2336 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
2337 e.SetAttribute ("cref", cref);
2338 e.InnerXml = "To be added; from:\n" + indent + "<see cref=\"" +
2339 string.Join ("\" />,\n" + indent + "<see cref=\"",
2340 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
2341 .OrderBy (s => s)) +
2343 docs.AppendChild (e);
2345 SortXmlNodes (docs, docs.SelectNodes ("exception"),
2346 new CrefComparer ());
2349 private static void NormalizeWhitespace(XmlElement e) {
2350 // Remove all text and whitespace nodes from the element so it
2351 // is outputted with nice indentation and no blank lines.
2352 ArrayList deleteNodes = new ArrayList();
2353 foreach (XmlNode n in e)
2354 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2356 foreach (XmlNode n in deleteNodes)
2357 n.ParentNode.RemoveChild(n);
2360 private static bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
2362 TypeDefinition type = member as TypeDefinition;
2364 type = member.DeclaringType as TypeDefinition;
2366 var versions = new string[] { GetAssemblyVersion (type.Module.Assembly) };
2368 if (root.LocalName == "AssemblyInfo")
2369 return UpdateAssemblyVersionForAssemblyInfo (root, root.ParentNode as XmlElement, versions, add: true);
2371 return UpdateAssemblyVersions (root, type.Module.Assembly, versions, add);
2374 private static string GetAssemblyVersion (AssemblyDefinition assembly)
2376 return assembly.Name.Version.ToString();
2379 private static bool UpdateAssemblyVersions(XmlElement root, AssemblyDefinition assembly, string[] assemblyVersions, bool add)
2381 XmlElement av = (XmlElement) root.SelectSingleNode ("AssemblyVersions");
2383 // AssemblyVersions is not part of the spec
2384 root.RemoveChild (av);
2387 string oldNodeFilter = "AssemblyInfo[not(@apistyle) or @apistyle='classic']";
2388 string newNodeFilter = "AssemblyInfo[@apistyle='unified']";
2389 string thisNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? newNodeFilter : oldNodeFilter;
2390 string thatNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? oldNodeFilter : newNodeFilter;
2392 XmlElement e = (XmlElement) root.SelectSingleNode (thisNodeFilter);
2394 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2396 if (MDocUpdater.HasDroppedNamespace (assembly)) {
2397 e.SetAttribute ("apistyle", "unified");
2400 root.AppendChild(e);
2403 var thatNode = (XmlElement) root.SelectSingleNode (thatNodeFilter);
2404 if (MDocUpdater.HasDroppedNamespace (assembly) && thatNode != null) {
2405 // there's a classic node, we should add apistyles
2406 e.SetAttribute ("apistyle", "unified");
2407 thatNode.SetAttribute ("apistyle", "classic");
2410 return UpdateAssemblyVersionForAssemblyInfo (e, root, assemblyVersions, add);
2413 static bool UpdateAssemblyVersionForAssemblyInfo (XmlElement e, XmlElement root, string[] assemblyVersions, bool add)
2415 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().Where (v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0).ToList ();
2416 // matches.Count > 0 && add: ignore -- already present
2417 if (matches.Count > 0 && !add) {
2418 foreach (XmlNode c in matches)
2421 else if (matches.Count == 0 && add) {
2422 foreach (string sv in assemblyVersions) {
2423 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2429 // matches.Count == 0 && !add: ignore -- already not present
2430 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2431 SortXmlNodes (e, avs, new VersionComparer ());
2433 bool anyNodesLeft = avs.Count != 0;
2434 if (!anyNodesLeft) {
2435 e.ParentNode.RemoveChild (e);
2437 return anyNodesLeft;
2440 // FIXME: get TypeReferences instead of string comparison?
2441 private static string[] IgnorableAttributes = {
2442 // Security related attributes
2443 "System.Reflection.AssemblyKeyFileAttribute",
2444 "System.Reflection.AssemblyDelaySignAttribute",
2445 // Present in @RefType
2446 "System.Runtime.InteropServices.OutAttribute",
2447 // For naming the indexer to use when not using indexers
2448 "System.Reflection.DefaultMemberAttribute",
2449 // for decimal constants
2450 "System.Runtime.CompilerServices.DecimalConstantAttribute",
2451 // compiler generated code
2452 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
2453 // more compiler generated code, e.g. iterator methods
2454 "System.Diagnostics.DebuggerHiddenAttribute",
2455 "System.Runtime.CompilerServices.FixedBufferAttribute",
2456 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
2457 // extension methods
2458 "System.Runtime.CompilerServices.ExtensionAttribute",
2459 // Used to differentiate 'object' from C#4 'dynamic'
2460 "System.Runtime.CompilerServices.DynamicAttribute",
2463 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes, TypeReference t=null)
2465 if (!attributes.Any ()) {
2466 ClearElement (root, "Attributes");
2470 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2474 e = root.OwnerDocument.CreateElement("Attributes");
2476 foreach (string attribute in attributes) {
2477 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2480 WriteElementText(ae, "AttributeName", attribute);
2483 if (e.ParentNode == null)
2484 root.AppendChild(e);
2486 NormalizeWhitespace(e);
2489 public static string MakeAttributesValueString (object v, TypeReference valueType)
2491 var formatters = new [] {
2492 new AttributeValueFormatter (),
2493 new ApplePlatformEnumFormatter (),
2494 new StandardFlagsEnumFormatter (),
2495 new DefaultAttributeValueFormatter (),
2498 ResolvedTypeInfo type = new ResolvedTypeInfo (valueType);
2499 foreach (var formatter in formatters) {
2500 string formattedValue;
2501 if (formatter.TryFormatValue (v, type, out formattedValue)) {
2502 return formattedValue;
2506 // this should never occur because the DefaultAttributeValueFormatter will always
2507 // successfully format the value ... but this is needed to satisfy the compiler :)
2508 throw new InvalidDataException (string.Format ("Unable to format attribute value ({0})", v.ToString ()));
2511 internal static IDictionary<long, string> GetEnumerationValues (TypeDefinition type)
2513 var values = new Dictionary<long, string> ();
2515 (from f in type.Fields
2516 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
2518 values [ToInt64 (f.Constant)] = f.Name;
2523 internal static long ToInt64 (object value)
2526 return (long) (ulong) value;
2527 return Convert.ToInt64 (value);
2530 private void MakeParameters (XmlElement root, MemberReference member, IList<ParameterDefinition> parameters, bool shouldDuplicateWithNew=false)
2532 XmlElement e = WriteElement(root, "Parameters");
2535 foreach (ParameterDefinition p in parameters) {
2539 var ptype = GetDocParameterType (p.ParameterType);
2540 var newPType = ptype;
2542 if (MDocUpdater.SwitchingToMagicTypes) {
2543 newPType = NativeTypeManager.ConvertFromNativeType (ptype);
2546 // now find the existing node, if it's there so we can reuse it.
2547 var nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2548 .Cast<XmlElement> ().Where (x => x.GetAttribute ("Name") == p.Name)
2551 if (nodes.Count () == 0) {
2552 // wasn't found, let's make sure it wasn't just cause the param name was changed
2553 nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2554 .Cast<XmlElement> ()
2555 .Skip (i) // this makes sure we don't inadvertently "reuse" nodes when adding new ones
2556 .Where (x => x.GetAttribute ("Name") != p.Name && (x.GetAttribute ("Type") == ptype || x.GetAttribute ("Type") == newPType))
2557 .Take(1) // there might be more than one that meets this parameter ... only take the first.
2562 x => x.GetAttribute ("Type") == ptype,
2563 x => x.SetAttribute ("Type", ptype),
2565 pe = root.OwnerDocument.CreateElement ("Parameter");
2568 pe.SetAttribute ("Name", p.Name);
2569 pe.SetAttribute ("Type", ptype);
2570 if (p.ParameterType is ByReferenceType) {
2572 pe.SetAttribute ("RefType", "out");
2574 pe.SetAttribute ("RefType", "ref");
2577 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
2586 private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams, MemberReference member, bool shouldDuplicateWithNew)
2588 if (typeParams == null || typeParams.Count == 0) {
2589 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2591 root.RemoveChild (f);
2594 XmlElement e = WriteElement(root, "TypeParameters");
2596 var nodes = e.SelectNodes ("TypeParameter").Cast<XmlElement> ().ToArray ();
2598 foreach (GenericParameter t in typeParams) {
2600 IList<TypeReference> constraints = t.Constraints;
2601 GenericParameterAttributes attrs = t.Attributes;
2607 var baseType = e.SelectSingleNode("BaseTypeName");
2608 // TODO: should this comparison take into account BaseTypeName?
2609 return x.GetAttribute("Name") == t.Name;
2611 x => {}, // no additional action required
2614 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2616 pe.SetAttribute("Name", t.Name);
2617 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""), t.DeclaringType);
2618 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2619 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2627 ce = root.OwnerDocument.CreateElement ("Constraints");
2629 pe.AppendChild (ce);
2630 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2631 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2632 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2633 AppendElementText (ce, "ParameterAttribute", "Covariant");
2634 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2635 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2636 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2637 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2638 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2639 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2640 foreach (TypeReference c in constraints) {
2641 TypeDefinition cd = c.Resolve ();
2642 AppendElementText (ce,
2643 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2644 GetDocTypeFullName (c));
2653 private void MakeParameters (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew)
2655 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2656 MakeParameters (root, mi, ((MethodDefinition)mi).Parameters, shouldDuplicateWithNew);
2657 else if (mi is MethodDefinition) {
2658 MethodDefinition mb = (MethodDefinition) mi;
2659 IList<ParameterDefinition> parameters = mb.Parameters;
2660 MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
2661 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2662 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2663 p.SetAttribute ("RefType", "this");
2666 else if (mi is PropertyDefinition) {
2667 IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
2668 if (parameters.Count > 0)
2669 MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
2673 else if (mi is FieldDefinition) return;
2674 else if (mi is EventDefinition) return;
2675 else throw new ArgumentException();
2678 internal static string GetDocParameterType (TypeReference type)
2680 return GetDocTypeFullName (type).Replace ("@", "&");
2683 private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes, bool shouldDuplicateWithNew=false)
2685 XmlElement e = WriteElement(root, "ReturnValue");
2686 var valueToUse = GetDocTypeFullName (type);
2688 AddXmlNode (e.SelectNodes("ReturnType").Cast<XmlElement> ().ToArray (),
2689 x => x.InnerText == valueToUse,
2690 x => x.InnerText = valueToUse,
2692 var newNode = WriteElementText(e, "ReturnType", valueToUse, forceNewElement: true);
2693 if (attributes != null)
2694 MakeAttributes(e, GetCustomAttributes (attributes, ""), type);
2701 private void MakeReturnValue (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew=false)
2703 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2705 else if (mi is MethodDefinition)
2706 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes, shouldDuplicateWithNew);
2707 else if (mi is PropertyDefinition)
2708 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null, shouldDuplicateWithNew);
2709 else if (mi is FieldDefinition)
2710 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null, shouldDuplicateWithNew);
2711 else if (mi is EventDefinition)
2712 MakeReturnValue (root, ((EventDefinition)mi).EventType, null, shouldDuplicateWithNew);
2714 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2717 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2719 MemberReference mi = info.Member;
2720 if (mi is TypeDefinition) return null;
2722 string sigs = memberFormatters [0].GetDeclaration (mi);
2723 if (sigs == null) return null; // not publicly visible
2725 // no documentation for property/event accessors. Is there a better way of doing this?
2726 if (mi.Name.StartsWith("get_")) return null;
2727 if (mi.Name.StartsWith("set_")) return null;
2728 if (mi.Name.StartsWith("add_")) return null;
2729 if (mi.Name.StartsWith("remove_")) return null;
2730 if (mi.Name.StartsWith("raise_")) return null;
2732 XmlElement me = doc.CreateElement("Member");
2733 me.SetAttribute("MemberName", GetMemberName (mi));
2737 if (exceptions.HasValue &&
2738 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2739 UpdateExceptions (info.Node, info.Member);
2741 if (since != null) {
2742 XmlNode docs = me.SelectSingleNode("Docs");
2743 docs.AppendChild (CreateSinceNode (doc));
2749 internal static string GetMemberName (MemberReference mi)
2751 MethodDefinition mb = mi as MethodDefinition;
2753 PropertyDefinition pi = mi as PropertyDefinition;
2756 return DocUtils.GetPropertyName (pi);
2758 StringBuilder sb = new StringBuilder (mi.Name.Length);
2759 if (!DocUtils.IsExplicitlyImplemented (mb))
2760 sb.Append (mi.Name);
2762 TypeReference iface;
2763 MethodReference ifaceMethod;
2764 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2765 sb.Append (GetDocTypeFullName (iface));
2767 sb.Append (ifaceMethod.Name);
2769 if (mb.IsGenericMethod ()) {
2770 IList<GenericParameter> typeParams = mb.GenericParameters;
2771 if (typeParams.Count > 0) {
2773 sb.Append (typeParams [0].Name);
2774 for (int i = 1; i < typeParams.Count; ++i)
2775 sb.Append (",").Append (typeParams [i].Name);
2779 return sb.ToString ();
2782 /// SIGNATURE GENERATION FUNCTIONS
2783 internal static bool IsPrivate (MemberReference mi)
2785 return memberFormatters [0].GetDeclaration (mi) == null;
2788 internal static string GetMemberType (MemberReference mi)
2790 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2791 return "Constructor";
2792 if (mi is MethodDefinition)
2794 if (mi is PropertyDefinition)
2796 if (mi is FieldDefinition)
2798 if (mi is EventDefinition)
2800 throw new ArgumentException();
2803 private static string GetDocTypeName (TypeReference type)
2805 return docTypeFormatter.GetName (type);
2808 internal static string GetDocTypeFullName (TypeReference type)
2810 return DocTypeFullMemberFormatter.Default.GetName (type);
2813 internal static string GetXPathForMember (DocumentationMember member)
2815 StringBuilder xpath = new StringBuilder ();
2816 xpath.Append ("//Members/Member[@MemberName=\"")
2817 .Append (member.MemberName)
2819 if (member.Parameters != null && member.Parameters.Count > 0) {
2820 xpath.Append ("/Parameters[count(Parameter) = ")
2821 .Append (member.Parameters.Count);
2822 for (int i = 0; i < member.Parameters.Count; ++i) {
2823 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2824 xpath.Append (member.Parameters [i]);
2825 xpath.Append ("\"");
2827 xpath.Append ("]/..");
2829 return xpath.ToString ();
2832 public static string GetXPathForMember (XPathNavigator member)
2834 StringBuilder xpath = new StringBuilder ();
2835 xpath.Append ("//Type[@FullName=\"")
2836 .Append (member.SelectSingleNode ("../../@FullName").Value)
2838 xpath.Append ("Members/Member[@MemberName=\"")
2839 .Append (member.SelectSingleNode ("@MemberName").Value)
2841 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2842 if (parameters.Count > 0) {
2843 xpath.Append ("/Parameters[count(Parameter) = ")
2844 .Append (parameters.Count);
2846 while (parameters.MoveNext ()) {
2848 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2849 xpath.Append (parameters.Current.Value);
2850 xpath.Append ("\"");
2852 xpath.Append ("]/..");
2854 return xpath.ToString ();
2857 public static string GetXPathForMember (MemberReference member)
2859 StringBuilder xpath = new StringBuilder ();
2860 xpath.Append ("//Type[@FullName=\"")
2861 .Append (member.DeclaringType.FullName)
2863 xpath.Append ("Members/Member[@MemberName=\"")
2864 .Append (GetMemberName (member))
2867 IList<ParameterDefinition> parameters = null;
2868 if (member is MethodDefinition)
2869 parameters = ((MethodDefinition) member).Parameters;
2870 else if (member is PropertyDefinition) {
2871 parameters = ((PropertyDefinition) member).Parameters;
2873 if (parameters != null && parameters.Count > 0) {
2874 xpath.Append ("/Parameters[count(Parameter) = ")
2875 .Append (parameters.Count);
2876 for (int i = 0; i < parameters.Count; ++i) {
2877 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2878 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2879 xpath.Append ("\"");
2881 xpath.Append ("]/..");
2883 return xpath.ToString ();
2887 static class CecilExtensions {
2888 public static string GetDeclaringType(this CustomAttribute attribute)
2890 var type = attribute.Constructor.DeclaringType;
2891 var typeName = type.FullName;
2893 string translatedType = NativeTypeManager.GetTranslatedName (type);
2894 return translatedType;
2897 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
2899 foreach (var c in type.Methods.Where (m => m.IsConstructor))
2900 yield return (MemberReference) c;
2901 foreach (var e in type.Events)
2902 yield return (MemberReference) e;
2903 foreach (var f in type.Fields)
2904 yield return (MemberReference) f;
2905 foreach (var m in type.Methods.Where (m => !m.IsConstructor))
2906 yield return (MemberReference) m;
2907 foreach (var t in type.NestedTypes)
2908 yield return (MemberReference) t;
2909 foreach (var p in type.Properties)
2910 yield return (MemberReference) p;
2913 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
2915 return GetMembers (type).Where (m => m.Name == member);
2918 public static MemberReference GetMember (this TypeDefinition type, string member)
2920 return GetMembers (type, member).EnsureZeroOrOne ();
2923 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2925 if (source.Count () > 1)
2926 throw new InvalidOperationException ("too many matches");
2927 return source.FirstOrDefault ();
2930 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2933 .Where (m => m.Name == method)
2934 .EnsureZeroOrOne ();
2937 public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
2939 TypeDefinition def = type as TypeDefinition;
2941 return new MemberReference [0];
2942 CustomAttribute defMemberAttr = def.CustomAttributes
2943 .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
2944 if (defMemberAttr == null)
2945 return new MemberReference [0];
2946 string name = (string) defMemberAttr.ConstructorArguments [0].Value;
2947 return def.Properties
2948 .Where (p => p.Name == name)
2949 .Select (p => (MemberReference) p);
2952 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2954 return assembly.Modules.SelectMany (md => md.GetAllTypes ());
2957 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2959 return GetTypes (assembly)
2960 .Where (td => td.FullName == type)
2961 .EnsureZeroOrOne ();
2964 public static bool IsGenericType (this TypeReference type)
2966 return type.GenericParameters.Count > 0;
2969 public static bool IsGenericMethod (this MethodReference method)
2971 return method.GenericParameters.Count > 0;
2974 public static MemberReference Resolve (this MemberReference member)
2976 FieldReference fr = member as FieldReference;
2978 return fr.Resolve ();
2979 MethodReference mr = member as MethodReference;
2981 return mr.Resolve ();
2982 TypeReference tr = member as TypeReference;
2984 return tr.Resolve ();
2985 PropertyReference pr = member as PropertyReference;
2988 EventReference er = member as EventReference;
2991 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2994 public static TypeReference GetUnderlyingType (this TypeDefinition type)
2998 return type.Fields.First (f => f.Name == "value__").FieldType;
3001 public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
3003 return self.Types.SelectMany (t => t.GetAllTypes ());
3006 static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
3010 if (!self.HasNestedTypes)
3013 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
3023 static class DocUtils {
3025 public static bool DoesNotHaveApiStyle(this XmlElement element, ApiStyle style) {
3026 string styleString = style.ToString ().ToLowerInvariant ();
3027 string apistylevalue = element.GetAttribute ("apistyle");
3028 return apistylevalue != styleString || string.IsNullOrWhiteSpace(apistylevalue);
3030 public static bool HasApiStyle(this XmlElement element, ApiStyle style) {
3031 string styleString = style.ToString ().ToLowerInvariant ();
3032 return element.GetAttribute ("apistyle") == styleString;
3034 public static bool HasApiStyle(this XmlNode node, ApiStyle style)
3036 var attribute = node.Attributes ["apistyle"];
3037 return attribute != null && attribute.Value == style.ToString ().ToLowerInvariant ();
3039 public static void AddApiStyle(this XmlElement element, ApiStyle style) {
3040 string styleString = style.ToString ().ToLowerInvariant ();
3041 var existingValue = element.GetAttribute ("apistyle");
3042 if (string.IsNullOrWhiteSpace (existingValue) || existingValue != styleString) {
3043 element.SetAttribute ("apistyle", styleString);
3046 public static void RemoveApiStyle (this XmlElement element, ApiStyle style)
3048 string styleString = style.ToString ().ToLowerInvariant ();
3049 string existingValue = element.GetAttribute ("apistyle");
3050 if (string.IsNullOrWhiteSpace (existingValue) || existingValue == styleString) {
3051 element.RemoveAttribute ("apistyle");
3054 public static void RemoveApiStyle (this XmlNode node, ApiStyle style)
3056 var styleAttribute = node.Attributes ["apistyle"];
3057 if (styleAttribute != null && styleAttribute.Value == style.ToString ().ToLowerInvariant ()) {
3058 node.Attributes.Remove (styleAttribute);
3062 public static bool IsExplicitlyImplemented (MethodDefinition method)
3064 return method.IsPrivate && method.IsFinal && method.IsVirtual;
3067 public static string GetTypeDotMember (string name)
3069 int startType, startMethod;
3070 startType = startMethod = -1;
3071 for (int i = 0; i < name.Length; ++i) {
3072 if (name [i] == '.') {
3073 startType = startMethod;
3077 return name.Substring (startType+1);
3080 public static string GetMember (string name)
3082 int i = name.LastIndexOf ('.');
3085 return name.Substring (i+1);
3088 public static void GetInfoForExplicitlyImplementedMethod (
3089 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
3093 if (method.Overrides.Count != 1)
3094 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
3095 iface = method.Overrides [0].DeclaringType;
3096 ifaceMethod = method.Overrides [0];
3099 public static string GetPropertyName (PropertyDefinition pi)
3101 // Issue: (g)mcs-generated assemblies that explicitly implement
3102 // properties don't specify the full namespace, just the
3103 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
3104 MethodDefinition method = pi.GetMethod;
3106 method = pi.SetMethod;
3107 if (!IsExplicitlyImplemented (method))
3110 // Need to determine appropriate namespace for this member.
3111 TypeReference iface;
3112 MethodReference ifaceMethod;
3113 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3114 return string.Join (".", new string[]{
3115 DocTypeFullMemberFormatter.Default.GetName (iface),
3116 GetMember (pi.Name)});
3119 public static string GetNamespace (TypeReference type)
3121 if (type.GetElementType ().IsNested)
3122 type = type.GetElementType ();
3123 while (type != null && type.IsNested)
3124 type = type.DeclaringType;
3126 return string.Empty;
3128 string typeNS = type.Namespace;
3130 // first, make sure this isn't a type reference to another assembly/module
3132 bool isInAssembly = MDocUpdater.IsInAssemblies(type.Module.Name);
3133 if (isInAssembly && !typeNS.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (type)) {
3134 typeNS = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typeNS);
3139 public static string PathCombine (string dir, string path)
3145 return Path.Combine (dir, path);
3148 public static bool IsExtensionMethod (MethodDefinition method)
3151 method.CustomAttributes
3152 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
3153 && method.DeclaringType.CustomAttributes
3154 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
3157 public static bool IsDelegate (TypeDefinition type)
3159 TypeReference baseRef = type.BaseType;
3160 if (baseRef == null)
3162 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
3163 baseRef.FullName == "System.MulticastDelegate";
3166 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
3168 List<TypeReference> decls = new List<TypeReference> ();
3170 while (type.DeclaringType != null) {
3171 decls.Add (type.DeclaringType);
3172 type = type.DeclaringType;
3178 public static int GetGenericArgumentCount (TypeReference type)
3180 GenericInstanceType inst = type as GenericInstanceType;
3182 ? inst.GenericArguments.Count
3183 : type.GenericParameters.Count;
3186 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
3188 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
3189 List<TypeReference> userInterfaces = new List<TypeReference> ();
3190 foreach (TypeReference iface in type.Interfaces) {
3191 TypeReference lookup = iface.Resolve () ?? iface;
3192 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
3193 userInterfaces.Add (iface);
3195 return userInterfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()));
3198 private static string GetQualifiedTypeName (TypeReference type)
3200 return "[" + type.Scope.Name + "]" + type.FullName;
3203 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
3205 HashSet<string> inheritedInterfaces = new HashSet<string> ();
3206 Action<TypeDefinition> a = null;
3208 if (t == null) return;
3209 foreach (TypeReference r in t.Interfaces) {
3210 inheritedInterfaces.Add (GetQualifiedTypeName (r));
3214 TypeReference baseRef = type.BaseType;
3215 while (baseRef != null) {
3216 TypeDefinition baseDef = baseRef.Resolve ();
3217 if (baseDef != null) {
3219 baseRef = baseDef.BaseType;
3224 foreach (TypeReference r in type.Interfaces)
3226 return inheritedInterfaces;
3230 class DocsNodeInfo {
3231 public DocsNodeInfo (XmlElement node)
3236 public DocsNodeInfo (XmlElement node, TypeDefinition type)
3242 public DocsNodeInfo (XmlElement node, MemberReference member)
3245 SetMemberInfo (member);
3248 void SetType (TypeDefinition type)
3251 throw new ArgumentNullException ("type");
3253 GenericParameters = new List<GenericParameter> (type.GenericParameters);
3254 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
3255 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
3256 for (int i = 0; i < declTypes.Count - 1; ++i) {
3257 int remove = System.Math.Min (maxGenArgs,
3258 DocUtils.GetGenericArgumentCount (declTypes [i]));
3259 maxGenArgs -= remove;
3260 while (remove-- > 0)
3261 GenericParameters.RemoveAt (0);
3263 if (DocUtils.IsDelegate (type)) {
3264 Parameters = type.GetMethod("Invoke").Parameters;
3265 ReturnType = type.GetMethod("Invoke").ReturnType;
3266 ReturnIsReturn = true;
3270 void SetMemberInfo (MemberReference member)
3273 throw new ArgumentNullException ("member");
3274 ReturnIsReturn = true;
3278 if (member is MethodReference ) {
3279 MethodReference mr = (MethodReference) member;
3280 Parameters = mr.Parameters;
3281 if (mr.IsGenericMethod ()) {
3282 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
3285 else if (member is PropertyDefinition) {
3286 Parameters = ((PropertyDefinition) member).Parameters;
3289 if (member is MethodDefinition) {
3290 ReturnType = ((MethodDefinition) member).ReturnType;
3291 } else if (member is PropertyDefinition) {
3292 ReturnType = ((PropertyDefinition) member).PropertyType;
3293 ReturnIsReturn = false;
3296 // no remarks section for enum members
3297 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
3301 public TypeReference ReturnType;
3302 public List<GenericParameter> GenericParameters;
3303 public IList<ParameterDefinition> Parameters;
3304 public bool ReturnIsReturn;
3305 public XmlElement Node;
3306 public bool AddRemarks = true;
3307 public MemberReference Member;
3308 public TypeDefinition Type;
3310 public override string ToString ()
3312 return string.Format ("{0} - {1} - {2}", Type, Member, Node == null ? "no xml" : "with xml");
3316 class DocumentationEnumerator {
3318 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3320 return GetDocumentationTypes (assembly, forTypes, null);
3323 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3325 foreach (TypeDefinition type in assembly.GetTypes()) {
3326 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
3328 if (seen != null && seen.Contains (type.FullName))
3331 foreach (TypeDefinition nested in type.NestedTypes)
3332 yield return nested;
3336 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3338 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
3339 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
3340 oldmember.RemoveAttribute ("__monodocer-seen__");
3343 MemberReference m = GetMember (type, new DocumentationMember (oldmember));
3345 yield return new DocsNodeInfo (oldmember);
3348 yield return new DocsNodeInfo (oldmember, m);
3353 protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
3355 string membertype = member.MemberType;
3357 string returntype = member.ReturnType;
3359 string docName = member.MemberName;
3361 string[] docTypeParams = GetTypeParameters (docName, member.TypeParameters);
3363 // If we're using 'magic types', then we might get false positives ... in those cases, we keep searching
3364 MemberReference likelyCandidate = null;
3366 // Loop through all members in this type with the same name
3367 var reflectedMembers = GetReflectionMembers (type, docName).ToArray ();
3368 foreach (MemberReference mi in reflectedMembers) {
3369 bool matchedMagicType = false;
3370 if (mi is TypeDefinition) continue;
3371 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
3373 if (MDocUpdater.IsPrivate (mi))
3376 IList<ParameterDefinition> pis = null;
3377 string[] typeParams = null;
3378 if (mi is MethodDefinition) {
3379 MethodDefinition mb = (MethodDefinition) mi;
3380 pis = mb.Parameters;
3381 if (mb.IsGenericMethod ()) {
3382 IList<GenericParameter> args = mb.GenericParameters;
3383 typeParams = args.Select (p => p.Name).ToArray ();
3386 else if (mi is PropertyDefinition)
3387 pis = ((PropertyDefinition)mi).Parameters;
3389 // check type parameters
3390 int methodTcount = member.TypeParameters == null ? 0 : member.TypeParameters.Count;
3391 int reflectionTcount = typeParams == null ? 0 : typeParams.Length;
3392 if (methodTcount != reflectionTcount)
3395 // check member parameters
3396 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
3397 int pcount = pis == null ? 0 : pis.Count;
3398 if (mcount != pcount)
3401 MethodDefinition mDef = mi as MethodDefinition;
3402 if (mDef != null && !mDef.IsConstructor) {
3403 // Casting operators can overload based on return type.
3404 string rtype = GetReplacedString (
3405 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
3406 typeParams, docTypeParams);
3407 string originalRType = rtype;
3408 if (MDocUpdater.SwitchingToMagicTypes) {
3409 rtype = NativeTypeManager.ConvertFromNativeType (rtype);
3412 if ((returntype != rtype && originalRType == rtype) ||
3413 (MDocUpdater.SwitchingToMagicTypes && returntype != originalRType && returntype != rtype && originalRType != rtype)) {
3417 if (originalRType != rtype)
3418 matchedMagicType = true;
3424 for (int i = 0; i < pis.Count; i++) {
3425 string paramType = GetReplacedString (
3426 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
3427 typeParams, docTypeParams);
3429 // if magictypes, replace paramType to "classic value" ... so the comparison works
3430 string originalParamType = paramType;
3431 if (MDocUpdater.SwitchingToMagicTypes) {
3432 paramType = NativeTypeManager.ConvertFromNativeType (paramType);
3435 string xmlMemberType = member.Parameters [i];
3436 if ((!paramType.Equals(xmlMemberType) && paramType.Equals(originalParamType)) ||
3437 (MDocUpdater.SwitchingToMagicTypes && !originalParamType.Equals(xmlMemberType) && !paramType.Equals(xmlMemberType) && !paramType.Equals(originalParamType))) {
3439 // did not match ... if we're dropping the namespace, and the paramType has the dropped
3440 // namespace, we should see if it matches when added
3441 bool stillDoesntMatch = true;
3442 if (MDocUpdater.HasDroppedNamespace(type) && paramType.StartsWith (MDocUpdater.droppedNamespace)) {
3443 string withDroppedNs = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, xmlMemberType);
3445 stillDoesntMatch = withDroppedNs != paramType;
3448 if (stillDoesntMatch) {
3454 if (originalParamType != paramType)
3455 matchedMagicType = true;
3457 if (!good) continue;
3459 if (MDocUpdater.SwitchingToMagicTypes && likelyCandidate == null && matchedMagicType) {
3460 // 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
3461 likelyCandidate = mi;
3468 return likelyCandidate;
3471 static string[] GetTypeParameters (string docName, IEnumerable<string> knownParameters)
3473 if (docName [docName.Length-1] != '>')
3475 StringList types = new StringList ();
3476 int endToken = docName.Length-2;
3477 int i = docName.Length-2;
3479 if (docName [i] == ',' || docName [i] == '<') {
3480 types.Add (docName.Substring (i + 1, endToken - i));
3483 if (docName [i] == '<')
3488 var arrayTypes = types.ToArray ();
3490 if (knownParameters != null && knownParameters.Any () && arrayTypes.Length != knownParameters.Count ())
3491 return knownParameters.ToArray ();
3496 protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
3498 // In case of dropping the namespace, we have to remove the dropped NS
3499 // so that docName will match what's in the assembly/type
3500 if (MDocUpdater.HasDroppedNamespace (type) && docName.StartsWith(MDocUpdater.droppedNamespace + ".")) {
3501 int droppedNsLength = MDocUpdater.droppedNamespace.Length;
3502 docName = docName.Substring (droppedNsLength + 1, docName.Length - droppedNsLength - 1);
3505 // need to worry about 4 forms of //@MemberName values:
3506 // 1. "Normal" (non-generic) member names: GetEnumerator
3508 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
3509 // - try as-is, and try type.member (due to "kludge" for property
3511 // 3. "Normal" Generic member names: Sort<T> (CSC)
3512 // - need to remove generic parameters --> "Sort"
3513 // 4. Explicitly-implemented interface members for generic interfaces:
3514 // -- System.Collections.Generic.IEnumerable<T>.Current
3515 // - Try as-is, and try type.member, *keeping* the generic parameters.
3516 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
3517 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
3518 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
3519 // this as (1) or (2).
3520 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
3522 foreach (MemberReference mi in type.GetMembers (docName))
3524 if (CountChars (docName, '.') > 0)
3525 // might be a property; try only type.member instead of
3526 // namespace.type.member.
3527 foreach (MemberReference mi in
3528 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
3535 int startLt, startType, startMethod;
3536 startLt = startType = startMethod = -1;
3537 for (int i = 0; i < docName.Length; ++i) {
3538 switch (docName [i]) {
3547 if (numLt == 0 && (i + 1) < docName.Length)
3548 // there's another character in docName, so this <...> sequence is
3549 // probably part of a generic type -- case 4.
3553 startType = startMethod;
3559 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
3561 foreach (MemberReference mi in type.GetMembers (refName))
3565 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
3568 // If we _still_ haven't found it, we've hit another generic naming issue:
3569 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
3570 // explicitly-implemented METHOD names (not properties), e.g.
3571 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
3572 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
3573 // which the XML docs will contain.
3575 // Alas, we can't derive the Mono name from docName, so we need to iterate
3576 // over all member names, convert them into CSC format, and compare... :-(
3579 foreach (MemberReference mi in type.GetMembers ()) {
3580 if (MDocUpdater.GetMemberName (mi) == docName)
3585 static string GetReplacedString (string typeName, string[] from, string[] to)
3589 for (int i = 0; i < from.Length; ++i)
3590 typeName = typeName.Replace (from [i], to [i]);
3594 private static int CountChars (string s, char c)
3597 for (int i = 0; i < s.Length; ++i) {
3605 class EcmaDocumentationEnumerator : DocumentationEnumerator {
3610 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
3613 this.ecmadocs = ecmaDocs;
3616 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3618 HashSet<string> seen = new HashSet<string> ();
3619 return GetDocumentationTypes (assembly, forTypes, seen)
3620 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
3623 new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3626 while (ecmadocs.Read ()) {
3627 switch (ecmadocs.Name) {
3629 if (typeDepth == -1)
3630 typeDepth = ecmadocs.Depth;
3631 if (ecmadocs.NodeType != XmlNodeType.Element)
3633 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
3635 string typename = ecmadocs.GetAttribute ("FullName");
3636 string typename2 = MDocUpdater.GetTypeFileName (typename);
3637 if (forTypes != null &&
3638 forTypes.BinarySearch (typename) < 0 &&
3639 typename != typename2 &&
3640 forTypes.BinarySearch (typename2) < 0)
3643 if ((t = assembly.GetType (typename)) == null &&
3644 (t = assembly.GetType (typename2)) == null)
3646 seen.Add (typename);
3647 if (typename != typename2)
3648 seen.Add (typename2);
3649 Console.WriteLine (" Import: {0}", t.FullName);
3650 if (ecmadocs.Name != "Docs") {
3651 int depth = ecmadocs.Depth;
3652 while (ecmadocs.Read ()) {
3653 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
3657 if (!ecmadocs.IsStartElement ("Docs"))
3658 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
3668 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3670 return GetMembers (basefile, type)
3671 .Concat (base.GetDocumentationMembers (basefile, type));
3674 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
3676 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
3679 if (ecmadocs.IsEmptyElement)
3682 int membersDepth = ecmadocs.Depth;
3684 while (go && ecmadocs.Read ()) {
3685 switch (ecmadocs.Name) {
3687 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
3689 DocumentationMember dm = new DocumentationMember (ecmadocs);
3691 string xp = MDocUpdater.GetXPathForMember (dm);
3692 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
3694 if (oldmember == null) {
3695 m = GetMember (type, dm);
3697 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3698 type.FullName, dm.MemberSignatures ["C#"]);
3699 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
3702 // oldmember lookup may have failed due to type parameter renames.
3704 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
3705 if (oldmember == null) {
3706 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
3707 oldmember = basefile.CreateElement ("Member");
3708 oldmember.SetAttribute ("MemberName", dm.MemberName);
3709 members.AppendChild (oldmember);
3710 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
3711 XmlElement ms = basefile.CreateElement ("MemberSignature");
3712 ms.SetAttribute ("Language", key);
3713 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
3714 oldmember.AppendChild (ms);
3716 oldmember.SetAttribute ("__monodocer-seen__", "true");
3717 Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
3722 m = GetMember (type, new DocumentationMember (oldmember));
3724 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3725 type.FullName, dm.MemberSignatures ["C#"]);
3728 oldmember.SetAttribute ("__monodocer-seen__", "true");
3730 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
3731 if (ecmadocs.Name != "Docs")
3732 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
3737 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
3746 abstract class DocumentationImporter {
3748 public abstract void ImportDocumentation (DocsNodeInfo info);
3751 class MsxdocDocumentationImporter : DocumentationImporter {
3753 XmlDocument slashdocs;
3755 public MsxdocDocumentationImporter (string file)
3757 var xml = File.ReadAllText (file);
3759 // Ensure Unix line endings
3760 xml = xml.Replace ("\r", "");
3762 slashdocs = new XmlDocument();
3763 slashdocs.LoadXml (xml);
3766 public override void ImportDocumentation (DocsNodeInfo info)
3768 XmlNode elem = GetDocs (info.Member ?? info.Type);
3773 XmlElement e = info.Node;
3775 if (elem.SelectSingleNode("summary") != null)
3776 MDocUpdater.ClearElement(e, "summary");
3777 if (elem.SelectSingleNode("remarks") != null)
3778 MDocUpdater.ClearElement(e, "remarks");
3779 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3780 MDocUpdater.ClearElement(e, "value");
3781 MDocUpdater.ClearElement(e, "returns");
3784 foreach (XmlNode child in elem.ChildNodes) {
3785 switch (child.Name) {
3788 XmlAttribute name = child.Attributes ["name"];
3791 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3793 p2.InnerXml = child.InnerXml;
3796 // Occasionally XML documentation will use <returns/> on
3797 // properties, so let's try to normalize things.
3800 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3801 v.InnerXml = child.InnerXml;
3807 case "permission": {
3808 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3811 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3813 a = e.OwnerDocument.CreateElement (child.Name);
3814 a.SetAttribute ("cref", cref.Value);
3817 a.InnerXml = child.InnerXml;
3821 XmlAttribute cref = child.Attributes ["cref"];
3824 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3826 a = e.OwnerDocument.CreateElement ("altmember");
3827 a.SetAttribute ("cref", cref.Value);
3834 if (child.NodeType == XmlNodeType.Element &&
3835 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3838 MDocUpdater.CopyNode (child, e);
3845 private XmlNode GetDocs (MemberReference member)
3847 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3848 if (slashdocsig != null)
3849 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3854 class EcmaDocumentationImporter : DocumentationImporter {
3858 public EcmaDocumentationImporter (XmlReader ecmaDocs)
3860 this.ecmadocs = ecmaDocs;
3863 public override void ImportDocumentation (DocsNodeInfo info)
3865 if (!ecmadocs.IsStartElement ("Docs")) {
3869 XmlElement e = info.Node;
3871 int depth = ecmadocs.Depth;
3872 ecmadocs.ReadStartElement ("Docs");
3873 while (ecmadocs.Read ()) {
3874 if (ecmadocs.Name == "Docs") {
3875 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3878 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3880 if (!ecmadocs.IsStartElement ())
3882 switch (ecmadocs.Name) {
3885 string name = ecmadocs.GetAttribute ("name");
3888 XmlNode doc = e.SelectSingleNode (
3889 ecmadocs.Name + "[@name='" + name + "']");
3890 string value = ecmadocs.ReadInnerXml ();
3892 doc.InnerXml = value.Replace ("\r", "");
3899 string name = ecmadocs.Name;
3900 string cref = ecmadocs.GetAttribute ("cref");
3903 XmlNode doc = e.SelectSingleNode (
3904 ecmadocs.Name + "[@cref='" + cref + "']");
3905 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3907 doc.InnerXml = value;
3909 XmlElement n = e.OwnerDocument.CreateElement (name);
3910 n.SetAttribute ("cref", cref);
3917 string name = ecmadocs.Name;
3918 string xpath = ecmadocs.Name;
3919 StringList attributes = new StringList (ecmadocs.AttributeCount);
3920 if (ecmadocs.MoveToFirstAttribute ()) {
3922 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3923 } while (ecmadocs.MoveToNextAttribute ());
3924 ecmadocs.MoveToContent ();
3926 if (attributes.Count > 0) {
3927 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3929 XmlNode doc = e.SelectSingleNode (xpath);
3930 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3932 doc.InnerXml = value;
3935 XmlElement n = e.OwnerDocument.CreateElement (name);
3937 foreach (string a in attributes) {
3938 int eq = a.IndexOf ('=');
3939 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3950 class DocumentationMember {
3951 public StringToStringMap MemberSignatures = new StringToStringMap ();
3952 public string ReturnType;
3953 public StringList Parameters;
3954 public StringList TypeParameters;
3955 public string MemberName;
3956 public string MemberType;
3958 public DocumentationMember (XmlReader reader)
3960 MemberName = reader.GetAttribute ("MemberName");
3961 int depth = reader.Depth;
3963 StringList p = new StringList ();
3964 StringList tp = new StringList ();
3966 if (reader.NodeType != XmlNodeType.Element)
3969 bool shouldUse = true;
3971 string apistyle = reader.GetAttribute ("apistyle");
3972 shouldUse = string.IsNullOrWhiteSpace(apistyle) || apistyle == "classic"; // only use this tag if it's an 'classic' style node
3974 catch (Exception ex) {}
3975 switch (reader.Name) {
3976 case "MemberSignature":
3978 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3982 MemberType = reader.ReadElementString ();
3985 if (reader.Depth == depth + 2 && shouldUse)
3986 ReturnType = reader.ReadElementString ();
3989 if (reader.Depth == depth + 2 && shouldUse)
3990 p.Add (reader.GetAttribute ("Type"));
3992 case "TypeParameter":
3993 if (reader.Depth == depth + 2 && shouldUse)
3994 tp.Add (reader.GetAttribute ("Name"));
3997 if (reader.Depth == depth + 1)
4001 } while (go && reader.Read () && reader.Depth >= depth);
4006 TypeParameters = tp;
4008 DiscernTypeParameters ();
4012 public DocumentationMember (XmlNode node)
4014 MemberName = node.Attributes ["MemberName"].Value;
4015 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
4016 XmlAttribute l = n.Attributes ["Language"];
4017 XmlAttribute v = n.Attributes ["Value"];
4018 XmlAttribute apistyle = n.Attributes ["apistyle"];
4019 bool shouldUse = apistyle == null || apistyle.Value == "classic";
4020 if (l != null && v != null && shouldUse)
4021 MemberSignatures [l.Value] = v.Value;
4023 MemberType = node.SelectSingleNode ("MemberType").InnerText;
4024 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType[not(@apistyle) or @apistyle='classic']");
4026 ReturnType = rt.InnerText;
4027 XmlNodeList p = node.SelectNodes ("Parameters/Parameter[not(@apistyle) or @apistyle='classic']");
4029 Parameters = new StringList (p.Count);
4030 for (int i = 0; i < p.Count; ++i)
4031 Parameters.Add (p [i].Attributes ["Type"].Value);
4033 XmlNodeList tp = node.SelectNodes ("TypeParameters/TypeParameter[not(@apistyle) or @apistyle='classic']");
4035 TypeParameters = new StringList (tp.Count);
4036 for (int i = 0; i < tp.Count; ++i)
4037 TypeParameters.Add (tp [i].Attributes ["Name"].Value);
4040 DiscernTypeParameters ();
4044 void DiscernTypeParameters ()
4046 // see if we can discern the param list from the name
4047 if (MemberName.Contains ("<") && MemberName.EndsWith (">")) {
4048 var starti = MemberName.IndexOf ("<") + 1;
4049 var endi = MemberName.LastIndexOf (">");
4050 var paramlist = MemberName.Substring (starti, endi - starti);
4051 var tparams = paramlist.Split (new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
4052 TypeParameters = new StringList (tparams);
4057 public class DynamicParserContext {
4058 public ReadOnlyCollection<bool> TransformFlags;
4059 public int TransformIndex;
4061 public DynamicParserContext (ICustomAttributeProvider provider)
4064 if (provider.HasCustomAttributes &&
4065 (da = (provider.CustomAttributes.Cast<CustomAttribute>()
4066 .SingleOrDefault (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))) != null) {
4067 CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
4068 ? new CustomAttributeArgument [0]
4069 : (CustomAttributeArgument[]) da.ConstructorArguments [0].Value;
4071 TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool) t.Value).ToArray());
4076 public enum MemberFormatterState {
4078 WithinGenericTypeParameters,
4081 public abstract class MemberFormatter {
4083 public virtual string Language {
4087 public string GetName (MemberReference member)
4089 return GetName (member, null);
4092 public virtual string GetName (MemberReference member, DynamicParserContext context)
4094 TypeReference type = member as TypeReference;
4096 return GetTypeName (type, context);
4097 MethodReference method = member as MethodReference;
4098 if (method != null && method.Name == ".ctor") // method.IsConstructor
4099 return GetConstructorName (method);
4101 return GetMethodName (method);
4102 PropertyReference prop = member as PropertyReference;
4104 return GetPropertyName (prop);
4105 FieldReference field = member as FieldReference;
4107 return GetFieldName (field);
4108 EventReference e = member as EventReference;
4110 return GetEventName (e);
4111 throw new NotSupportedException ("Can't handle: " +
4112 (member == null ? "null" : member.GetType().ToString()));
4115 protected virtual string GetTypeName (TypeReference type)
4117 return GetTypeName (type, null);
4120 protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
4123 throw new ArgumentNullException ("type");
4124 return _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
4127 protected virtual char[] ArrayDelimeters {
4128 get {return new char[]{'[', ']'};}
4131 protected virtual MemberFormatterState MemberFormatterState { get; set; }
4133 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4135 if (type is ArrayType) {
4136 TypeSpecification spec = type as TypeSpecification;
4137 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
4138 return AppendArrayModifiers (buf, (ArrayType) type);
4140 if (type is ByReferenceType) {
4141 return AppendRefTypeName (buf, type, context);
4143 if (type is PointerType) {
4144 return AppendPointerTypeName (buf, type, context);
4146 if (type is GenericParameter) {
4147 return AppendTypeName (buf, type, context);
4149 AppendNamespace (buf, type);
4150 GenericInstanceType genInst = type as GenericInstanceType;
4151 if (type.GenericParameters.Count == 0 &&
4152 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
4153 return AppendFullTypeName (buf, type, context);
4155 return AppendGenericType (buf, type, context);
4158 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4160 string ns = DocUtils.GetNamespace (type);
4161 if (ns != null && ns.Length > 0)
4162 buf.Append (ns).Append ('.');
4166 protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4168 if (type.DeclaringType != null)
4169 AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
4170 return AppendTypeName (buf, type, context);
4173 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4175 if (context != null)
4176 context.TransformIndex++;
4177 return AppendTypeName (buf, type.Name);
4180 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
4182 int n = typename.IndexOf ("`");
4184 return buf.Append (typename.Substring (0, n));
4185 return buf.Append (typename);
4188 protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
4190 buf.Append (ArrayDelimeters [0]);
4191 int rank = array.Rank;
4193 buf.Append (new string (',', rank-1));
4194 return buf.Append (ArrayDelimeters [1]);
4197 protected virtual string RefTypeModifier {
4201 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4203 TypeSpecification spec = type as TypeSpecification;
4204 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4205 .Append (RefTypeModifier);
4208 protected virtual string PointerModifier {
4212 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4214 TypeSpecification spec = type as TypeSpecification;
4215 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4216 .Append (PointerModifier);
4219 protected virtual char[] GenericTypeContainer {
4220 get {return new char[]{'<', '>'};}
4223 protected virtual char NestedTypeSeparator {
4227 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4229 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4230 type is GenericInstanceType ? type.GetElementType () : type);
4231 List<TypeReference> genArgs = GetGenericArguments (type);
4234 bool insertNested = false;
4235 foreach (var decl in decls) {
4236 TypeReference declDef = decl.Resolve () ?? decl;
4238 buf.Append (NestedTypeSeparator);
4240 insertNested = true;
4241 AppendTypeName (buf, declDef, context);
4242 int ac = DocUtils.GetGenericArgumentCount (declDef);
4246 buf.Append (GenericTypeContainer [0]);
4247 var origState = MemberFormatterState;
4248 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4249 _AppendTypeName (buf, genArgs [argIdx++], context);
4250 for (int i = 1; i < c; ++i) {
4251 _AppendTypeName (buf.Append (","), genArgs [argIdx++], context);
4253 MemberFormatterState = origState;
4254 buf.Append (GenericTypeContainer [1]);
4260 protected List<TypeReference> GetGenericArguments (TypeReference type)
4262 var args = new List<TypeReference> ();
4263 GenericInstanceType inst = type as GenericInstanceType;
4265 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
4267 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
4271 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4276 protected virtual string GetConstructorName (MethodReference constructor)
4278 return constructor.Name;
4281 protected virtual string GetMethodName (MethodReference method)
4286 protected virtual string GetPropertyName (PropertyReference property)
4288 return property.Name;
4291 protected virtual string GetFieldName (FieldReference field)
4296 protected virtual string GetEventName (EventReference e)
4301 public virtual string GetDeclaration (MemberReference member)
4304 throw new ArgumentNullException ("member");
4305 TypeDefinition type = member as TypeDefinition;
4307 return GetTypeDeclaration (type);
4308 MethodDefinition method = member as MethodDefinition;
4309 if (method != null && method.IsConstructor)
4310 return GetConstructorDeclaration (method);
4312 return GetMethodDeclaration (method);
4313 PropertyDefinition prop = member as PropertyDefinition;
4315 return GetPropertyDeclaration (prop);
4316 FieldDefinition field = member as FieldDefinition;
4318 return GetFieldDeclaration (field);
4319 EventDefinition e = member as EventDefinition;
4321 return GetEventDeclaration (e);
4322 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
4325 protected virtual string GetTypeDeclaration (TypeDefinition type)
4328 throw new ArgumentNullException ("type");
4329 StringBuilder buf = new StringBuilder (type.Name.Length);
4330 _AppendTypeName (buf, type, null);
4331 AppendGenericTypeConstraints (buf, type);
4332 return buf.ToString ();
4335 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
4337 return GetConstructorName (constructor);
4340 protected virtual string GetMethodDeclaration (MethodDefinition method)
4342 if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
4343 ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
4346 // Special signature for destructors.
4347 if (method.Name == "Finalize" && method.Parameters.Count == 0)
4348 return GetFinalizerName (method);
4350 StringBuilder buf = new StringBuilder ();
4352 AppendVisibility (buf, method);
4353 if (buf.Length == 0 &&
4354 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
4357 AppendModifiers (buf, method);
4359 if (buf.Length != 0)
4361 buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
4363 AppendMethodName (buf, method);
4364 AppendGenericMethod (buf, method).Append (" ");
4365 AppendParameters (buf, method, method.Parameters);
4366 AppendGenericMethodConstraints (buf, method);
4367 return buf.ToString ();
4370 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4372 return buf.Append (method.Name);
4375 protected virtual string GetFinalizerName (MethodDefinition method)
4380 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4385 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4390 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4395 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4400 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4405 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
4407 return GetPropertyName (property);
4410 protected virtual string GetFieldDeclaration (FieldDefinition field)
4412 return GetFieldName (field);
4415 protected virtual string GetEventDeclaration (EventDefinition e)
4417 return GetEventName (e);
4421 class ILFullMemberFormatter : MemberFormatter {
4423 public override string Language {
4424 get {return "ILAsm";}
4427 protected override char NestedTypeSeparator {
4433 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4435 if (GetBuiltinType (type.FullName) != null)
4437 string ns = DocUtils.GetNamespace (type);
4438 if (ns != null && ns.Length > 0) {
4439 if (type.IsValueType)
4440 buf.Append ("valuetype ");
4442 buf.Append ("class ");
4443 buf.Append (ns).Append ('.');
4448 protected static string GetBuiltinType (string t)
4451 case "System.Byte": return "unsigned int8";
4452 case "System.SByte": return "int8";
4453 case "System.Int16": return "int16";
4454 case "System.Int32": return "int32";
4455 case "System.Int64": return "int64";
4456 case "System.IntPtr": return "native int";
4458 case "System.UInt16": return "unsigned int16";
4459 case "System.UInt32": return "unsigned int32";
4460 case "System.UInt64": return "unsigned int64";
4461 case "System.UIntPtr": return "native unsigned int";
4463 case "System.Single": return "float32";
4464 case "System.Double": return "float64";
4465 case "System.Boolean": return "bool";
4466 case "System.Char": return "char";
4467 case "System.Void": return "void";
4468 case "System.String": return "string";
4469 case "System.Object": return "object";
4474 protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
4476 return buf.Append (typename);
4479 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4481 if (type is GenericParameter) {
4482 AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
4486 string s = GetBuiltinType (type.FullName);
4488 return buf.Append (s);
4490 return base.AppendTypeName (buf, type, context);
4493 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
4495 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
4496 return buf.Append (type.Owner is TypeReference ? "!" : "!!");
4498 GenericParameterAttributes attrs = type.Attributes;
4499 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
4500 buf.Append ("class ");
4501 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
4502 buf.Append ("struct ");
4503 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
4504 buf.Append (".ctor ");
4505 IList<TypeReference> constraints = type.Constraints;
4506 MemberFormatterState = 0;
4507 if (constraints.Count > 0) {
4508 var full = new ILFullMemberFormatter ();
4509 buf.Append ("(").Append (full.GetName (constraints [0]));
4510 for (int i = 1; i < constraints.Count; ++i) {
4511 buf.Append (", ").Append (full.GetName (constraints [i]));
4515 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4517 if ((attrs & GenericParameterAttributes.Covariant) != 0)
4519 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
4524 protected override string GetTypeDeclaration (TypeDefinition type)
4526 string visibility = GetTypeVisibility (type.Attributes);
4527 if (visibility == null)
4530 StringBuilder buf = new StringBuilder ();
4532 buf.Append (".class ");
4534 buf.Append ("nested ");
4535 buf.Append (visibility).Append (" ");
4536 if (type.IsInterface)
4537 buf.Append ("interface ");
4538 if (type.IsSequentialLayout)
4539 buf.Append ("sequential ");
4540 if (type.IsAutoLayout)
4541 buf.Append ("auto ");
4542 if (type.IsAnsiClass)
4543 buf.Append ("ansi ");
4544 if (type.IsAbstract)
4545 buf.Append ("abstract ");
4546 if (type.IsSerializable)
4547 buf.Append ("serializable ");
4549 buf.Append ("sealed ");
4550 if (type.IsBeforeFieldInit)
4551 buf.Append ("beforefieldinit ");
4552 var state = MemberFormatterState;
4553 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4554 buf.Append (GetName (type));
4555 MemberFormatterState = state;
4556 var full = new ILFullMemberFormatter ();
4557 if (type.BaseType != null) {
4558 buf.Append (" extends ");
4559 if (type.BaseType.FullName == "System.Object")
4560 buf.Append ("System.Object");
4562 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
4565 foreach (var name in type.Interfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()))
4566 .Select (i => full.GetName (i))
4567 .OrderBy (n => n)) {
4569 buf.Append (" implements ");
4578 return buf.ToString ();
4581 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4583 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4584 type is GenericInstanceType ? type.GetElementType () : type);
4586 foreach (var decl in decls) {
4587 TypeReference declDef = decl.Resolve () ?? decl;
4589 buf.Append (NestedTypeSeparator);
4592 AppendTypeName (buf, declDef, context);
4596 foreach (TypeReference arg in GetGenericArguments (type)) {
4600 _AppendTypeName (buf, arg, context);
4606 static string GetTypeVisibility (TypeAttributes ta)
4608 switch (ta & TypeAttributes.VisibilityMask) {
4609 case TypeAttributes.Public:
4610 case TypeAttributes.NestedPublic:
4613 case TypeAttributes.NestedFamily:
4614 case TypeAttributes.NestedFamORAssem:
4622 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4624 return GetMethodDeclaration (constructor);
4627 protected override string GetMethodDeclaration (MethodDefinition method)
4629 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
4632 var buf = new StringBuilder ();
4633 buf.Append (".method ");
4634 AppendVisibility (buf, method);
4635 if (method.IsStatic)
4636 buf.Append ("static ");
4637 if (method.IsHideBySig)
4638 buf.Append ("hidebysig ");
4639 if (method.IsPInvokeImpl) {
4640 var info = method.PInvokeInfo;
4641 buf.Append ("pinvokeimpl (\"")
4642 .Append (info.Module.Name)
4643 .Append ("\" as \"")
4644 .Append (info.EntryPoint)
4646 if (info.IsCharSetAuto)
4647 buf.Append (" auto");
4648 if (info.IsCharSetUnicode)
4649 buf.Append (" unicode");
4650 if (info.IsCharSetAnsi)
4651 buf.Append (" ansi");
4652 if (info.IsCallConvCdecl)
4653 buf.Append (" cdecl");
4654 if (info.IsCallConvStdCall)
4655 buf.Append (" stdcall");
4656 if (info.IsCallConvWinapi)
4657 buf.Append (" winapi");
4658 if (info.IsCallConvThiscall)
4659 buf.Append (" thiscall");
4660 if (info.SupportsLastError)
4661 buf.Append (" lasterr");
4664 if (method.IsSpecialName)
4665 buf.Append ("specialname ");
4666 if (method.IsRuntimeSpecialName)
4667 buf.Append ("rtspecialname ");
4668 if (method.IsNewSlot)
4669 buf.Append ("newslot ");
4670 if (method.IsVirtual)
4671 buf.Append ("virtual ");
4672 if (!method.IsStatic)
4673 buf.Append ("instance ");
4674 _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
4676 .Append (method.Name);
4677 if (method.IsGenericMethod ()) {
4678 var state = MemberFormatterState;
4679 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4680 IList<GenericParameter> args = method.GenericParameters;
4681 if (args.Count > 0) {
4683 _AppendTypeName (buf, args [0], null);
4684 for (int i = 1; i < args.Count; ++i)
4685 _AppendTypeName (buf.Append (", "), args [i], null);
4688 MemberFormatterState = state;
4693 for (int i = 0; i < method.Parameters.Count; ++i) {
4697 _AppendTypeName (buf, method.Parameters [i].ParameterType, new DynamicParserContext (method.Parameters [i]));
4699 buf.Append (method.Parameters [i].Name);
4703 buf.Append (" cil");
4704 if (method.IsRuntime)
4705 buf.Append (" runtime");
4706 if (method.IsManaged)
4707 buf.Append (" managed");
4709 return buf.ToString ();
4712 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4714 if (DocUtils.IsExplicitlyImplemented (method)) {
4715 TypeReference iface;
4716 MethodReference ifaceMethod;
4717 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4718 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4720 .Append (ifaceMethod.Name);
4722 return base.AppendMethodName (buf, method);
4725 protected override string RefTypeModifier {
4729 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4731 if (method.IsPublic)
4732 return buf.Append ("public ");
4733 if (method.IsFamilyAndAssembly)
4734 return buf.Append ("familyandassembly");
4735 if (method.IsFamilyOrAssembly)
4736 return buf.Append ("familyorassembly");
4737 if (method.IsFamily)
4738 return buf.Append ("family");
4742 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4744 string modifiers = String.Empty;
4745 if (method.IsStatic) modifiers += " static";
4746 if (method.IsVirtual && !method.IsAbstract) {
4747 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4748 else modifiers += " override";
4750 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4751 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4752 if (method.IsFinal) modifiers += " sealed";
4753 if (modifiers == " virtual sealed") modifiers = "";
4755 return buf.Append (modifiers);
4758 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4760 if (method.IsGenericMethod ()) {
4761 IList<GenericParameter> args = method.GenericParameters;
4762 if (args.Count > 0) {
4764 buf.Append (args [0].Name);
4765 for (int i = 1; i < args.Count; ++i)
4766 buf.Append (",").Append (args [i].Name);
4773 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4775 return AppendParameters (buf, method, parameters, '(', ')');
4778 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4782 if (parameters.Count > 0) {
4783 if (DocUtils.IsExtensionMethod (method))
4784 buf.Append ("this ");
4785 AppendParameter (buf, parameters [0]);
4786 for (int i = 1; i < parameters.Count; ++i) {
4788 AppendParameter (buf, parameters [i]);
4792 return buf.Append (end);
4795 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4797 if (parameter.ParameterType is ByReferenceType) {
4798 if (parameter.IsOut)
4799 buf.Append ("out ");
4801 buf.Append ("ref ");
4803 buf.Append (GetName (parameter.ParameterType)).Append (" ");
4804 return buf.Append (parameter.Name);
4807 protected override string GetPropertyDeclaration (PropertyDefinition property)
4809 MethodDefinition gm = null, sm = null;
4811 string get_visible = null;
4812 if ((gm = property.GetMethod) != null &&
4813 (DocUtils.IsExplicitlyImplemented (gm) ||
4814 (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
4815 get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
4816 string set_visible = null;
4817 if ((sm = property.SetMethod) != null &&
4818 (DocUtils.IsExplicitlyImplemented (sm) ||
4819 (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
4820 set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
4822 if ((set_visible == null) && (get_visible == null))
4825 StringBuilder buf = new StringBuilder ()
4826 .Append (".property ");
4827 if (!(gm ?? sm).IsStatic)
4828 buf.Append ("instance ");
4829 _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
4830 buf.Append (' ').Append (property.Name);
4831 if (!property.HasParameters || property.Parameters.Count == 0)
4832 return buf.ToString ();
4836 foreach (ParameterDefinition p in property.Parameters) {
4840 _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
4844 return buf.ToString ();
4847 protected override string GetFieldDeclaration (FieldDefinition field)
4849 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4850 if (declType.IsEnum && field.Name == "value__")
4851 return null; // This member of enums aren't documented.
4853 StringBuilder buf = new StringBuilder ();
4854 AppendFieldVisibility (buf, field);
4855 if (buf.Length == 0)
4858 buf.Insert (0, ".field ");
4861 buf.Append ("static ");
4862 if (field.IsInitOnly)
4863 buf.Append ("initonly ");
4864 if (field.IsLiteral)
4865 buf.Append ("literal ");
4866 _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
4867 buf.Append (' ').Append (field.Name);
4868 AppendFieldValue (buf, field);
4870 return buf.ToString ();
4873 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4876 return buf.Append ("public ");
4877 if (field.IsFamilyAndAssembly)
4878 return buf.Append ("familyandassembly ");
4879 if (field.IsFamilyOrAssembly)
4880 return buf.Append ("familyorassembly ");
4882 return buf.Append ("family ");
4886 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4888 // enums have a value__ field, which we ignore
4889 if (field.DeclaringType.IsGenericType ())
4891 if (field.HasConstant && field.IsLiteral) {
4894 val = field.Constant;
4899 buf.Append (" = ").Append ("null");
4900 else if (val is Enum)
4902 .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4904 .Append (val.ToString ())
4906 else if (val is IFormattable) {
4907 string value = ((IFormattable)val).ToString();
4910 buf.Append ("\"" + value + "\"");
4912 buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4921 protected override string GetEventDeclaration (EventDefinition e)
4923 StringBuilder buf = new StringBuilder ();
4924 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4929 buf.Append (".event ")
4930 .Append (GetName (e.EventType))
4934 return buf.ToString ();
4938 class ILMemberFormatter : ILFullMemberFormatter {
4939 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4945 class ILNativeTypeMemberFormatter : ILFullMemberFormatter {
4946 protected static string _GetBuiltinType (string t)
4948 //string moddedType = base.GetBuiltinType (t);
4950 //return moddedType;
4954 class CSharpNativeTypeMemberFormatter : CSharpFullMemberFormatter {
4955 protected override string GetCSharpType (string t) {
4956 string moddedType = base.GetCSharpType (t);
4958 switch (moddedType) {
4959 case "int": return "nint";
4964 case "System.Drawing.SizeF":
4965 return "CoreGraphics.CGSize";
4966 case "System.Drawing.PointF":
4967 return "CoreGraphics.CGPoint";
4968 case "System.Drawing.RectangleF":
4969 return "CoreGraphics.CGPoint";
4975 class CSharpFullMemberFormatter : MemberFormatter {
4977 public override string Language {
4981 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4984 string ns = DocUtils.GetNamespace (type);
4985 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
4986 buf.Append (ns).Append ('.');
4990 protected virtual string GetCSharpType (string t)
4993 case "System.Byte": return "byte";
4994 case "System.SByte": return "sbyte";
4995 case "System.Int16": return "short";
4996 case "System.Int32": return "int";
4997 case "System.Int64": return "long";
4999 case "System.UInt16": return "ushort";
5000 case "System.UInt32": return "uint";
5001 case "System.UInt64": return "ulong";
5003 case "System.Single": return "float";
5004 case "System.Double": return "double";
5005 case "System.Decimal": return "decimal";
5006 case "System.Boolean": return "bool";
5007 case "System.Char": return "char";
5008 case "System.Void": return "void";
5009 case "System.String": return "string";
5010 case "System.Object": return "object";
5015 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
5017 if (context != null && context.TransformFlags != null &&
5018 (context.TransformFlags.Count == 0 || context.TransformFlags [context.TransformIndex])) {
5019 context.TransformIndex++;
5020 return buf.Append ("dynamic");
5023 if (type is GenericParameter)
5024 return AppendGenericParameterConstraints (buf, (GenericParameter) type, context).Append (type.Name);
5025 string t = type.FullName;
5026 if (!t.StartsWith ("System.")) {
5027 return base.AppendTypeName (buf, type, context);
5030 string s = GetCSharpType (t);
5032 if (context != null)
5033 context.TransformIndex++;
5034 return buf.Append (s);
5037 return base.AppendTypeName (buf, type, context);
5040 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
5042 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
5044 GenericParameterAttributes attrs = type.Attributes;
5045 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
5046 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
5050 buf.Append ("out ");
5054 protected override string GetTypeDeclaration (TypeDefinition type)
5056 string visibility = GetTypeVisibility (type.Attributes);
5057 if (visibility == null)
5060 StringBuilder buf = new StringBuilder ();
5062 buf.Append (visibility);
5065 MemberFormatter full = new CSharpFullMemberFormatter ();
5067 if (DocUtils.IsDelegate (type)) {
5068 buf.Append("delegate ");
5069 MethodDefinition invoke = type.GetMethod ("Invoke");
5070 buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
5071 buf.Append (GetName (type));
5072 AppendParameters (buf, invoke, invoke.Parameters);
5073 AppendGenericTypeConstraints (buf, type);
5076 return buf.ToString();
5079 if (type.IsAbstract && !type.IsInterface)
5080 buf.Append("abstract ");
5081 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
5082 buf.Append("sealed ");
5083 buf.Replace ("abstract sealed", "static");
5085 buf.Append (GetTypeKind (type));
5087 buf.Append (GetCSharpType (type.FullName) == null
5092 TypeReference basetype = type.BaseType;
5093 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
5096 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
5097 .Select (iface => full.GetName (iface))
5101 if (basetype != null || interface_names.Count > 0)
5104 if (basetype != null) {
5105 buf.Append (full.GetName (basetype));
5106 if (interface_names.Count > 0)
5110 for (int i = 0; i < interface_names.Count; i++){
5113 buf.Append (interface_names [i]);
5115 AppendGenericTypeConstraints (buf, type);
5118 return buf.ToString ();
5121 static string GetTypeKind (TypeDefinition t)
5127 if (t.IsClass || t.FullName == "System.Enum")
5131 throw new ArgumentException(t.FullName);
5134 static string GetTypeVisibility (TypeAttributes ta)
5136 switch (ta & TypeAttributes.VisibilityMask) {
5137 case TypeAttributes.Public:
5138 case TypeAttributes.NestedPublic:
5141 case TypeAttributes.NestedFamily:
5142 case TypeAttributes.NestedFamORAssem:
5150 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
5152 if (type.GenericParameters.Count == 0)
5154 return AppendConstraints (buf, type.GenericParameters);
5157 private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
5159 foreach (GenericParameter genArg in genArgs) {
5160 GenericParameterAttributes attrs = genArg.Attributes;
5161 IList<TypeReference> constraints = genArg.Constraints;
5162 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
5165 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
5166 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
5167 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
5170 if (!isref && !isvt && !isnew && constraints.Count == 0)
5172 buf.Append (" where ").Append (genArg.Name).Append (" : ");
5174 buf.Append ("class");
5178 buf.Append ("struct");
5181 if (constraints.Count > 0 && !isvt) {
5184 buf.Append (GetTypeName (constraints [0]));
5185 for (int i = 1; i < constraints.Count; ++i)
5186 buf.Append (", ").Append (GetTypeName (constraints [i]));
5188 if (isnew && !isvt) {
5191 buf.Append ("new()");
5197 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5199 StringBuilder buf = new StringBuilder ();
5200 AppendVisibility (buf, constructor);
5201 if (buf.Length == 0)
5205 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
5206 AppendParameters (buf, constructor, constructor.Parameters);
5209 return buf.ToString ();
5212 protected override string GetMethodDeclaration (MethodDefinition method)
5214 string decl = base.GetMethodDeclaration (method);
5220 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
5222 if (DocUtils.IsExplicitlyImplemented (method)) {
5223 TypeReference iface;
5224 MethodReference ifaceMethod;
5225 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5226 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
5228 .Append (ifaceMethod.Name);
5230 return base.AppendMethodName (buf, method);
5233 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
5235 if (method.GenericParameters.Count == 0)
5237 return AppendConstraints (buf, method.GenericParameters);
5240 protected override string RefTypeModifier {
5244 protected override string GetFinalizerName (MethodDefinition method)
5246 return "~" + method.DeclaringType.Name + " ()";
5249 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
5253 if (method.IsPublic)
5254 return buf.Append ("public");
5255 if (method.IsFamily || method.IsFamilyOrAssembly)
5256 return buf.Append ("protected");
5260 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
5262 string modifiers = String.Empty;
5263 if (method.IsStatic) modifiers += " static";
5264 if (method.IsVirtual && !method.IsAbstract) {
5265 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
5266 else modifiers += " override";
5268 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
5269 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
5270 if (method.IsFinal) modifiers += " sealed";
5271 if (modifiers == " virtual sealed") modifiers = "";
5273 return buf.Append (modifiers);
5276 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
5278 if (method.IsGenericMethod ()) {
5279 IList<GenericParameter> args = method.GenericParameters;
5280 if (args.Count > 0) {
5282 buf.Append (args [0].Name);
5283 for (int i = 1; i < args.Count; ++i)
5284 buf.Append (",").Append (args [i].Name);
5291 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
5293 return AppendParameters (buf, method, parameters, '(', ')');
5296 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
5300 if (parameters.Count > 0) {
5301 if (DocUtils.IsExtensionMethod (method))
5302 buf.Append ("this ");
5303 AppendParameter (buf, parameters [0]);
5304 for (int i = 1; i < parameters.Count; ++i) {
5306 AppendParameter (buf, parameters [i]);
5310 return buf.Append (end);
5313 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
5315 if (parameter.ParameterType is ByReferenceType) {
5316 if (parameter.IsOut)
5317 buf.Append ("out ");
5319 buf.Append ("ref ");
5321 buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
5322 buf.Append (parameter.Name);
5323 if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant) {
5324 buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
5329 protected override string GetPropertyDeclaration (PropertyDefinition property)
5331 MethodDefinition method;
5333 string get_visible = null;
5334 if ((method = property.GetMethod) != null &&
5335 (DocUtils.IsExplicitlyImplemented (method) ||
5336 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5337 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5338 string set_visible = null;
5339 if ((method = property.SetMethod) != null &&
5340 (DocUtils.IsExplicitlyImplemented (method) ||
5341 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5342 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5344 if ((set_visible == null) && (get_visible == null))
5348 StringBuilder buf = new StringBuilder ();
5349 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
5350 buf.Append (visibility = get_visible);
5351 else if (set_visible != null && get_visible == null)
5352 buf.Append (visibility = set_visible);
5354 buf.Append (visibility = "public");
5356 // Pick an accessor to use for static/virtual/override/etc. checks.
5357 method = property.SetMethod;
5359 method = property.GetMethod;
5361 string modifiers = String.Empty;
5362 if (method.IsStatic) modifiers += " static";
5363 if (method.IsVirtual && !method.IsAbstract) {
5364 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
5365 modifiers += " virtual";
5367 modifiers += " override";
5369 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
5370 if (method.IsAbstract && !declDef.IsInterface)
5371 modifiers += " abstract";
5373 modifiers += " sealed";
5374 if (modifiers == " virtual sealed")
5376 buf.Append (modifiers).Append (' ');
5378 buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
5380 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
5381 string name = property.Name;
5382 foreach (MemberReference mi in defs) {
5383 if (mi == property) {
5388 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
5390 if (property.Parameters.Count != 0) {
5391 AppendParameters (buf, method, property.Parameters, '[', ']');
5395 if (get_visible != null) {
5396 if (get_visible != visibility)
5397 buf.Append (' ').Append (get_visible);
5398 buf.Append (" get;");
5400 if (set_visible != null) {
5401 if (set_visible != visibility)
5402 buf.Append (' ').Append (set_visible);
5403 buf.Append (" set;");
5407 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
5410 protected override string GetFieldDeclaration (FieldDefinition field)
5412 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
5413 if (declType.IsEnum && field.Name == "value__")
5414 return null; // This member of enums aren't documented.
5416 StringBuilder buf = new StringBuilder ();
5417 AppendFieldVisibility (buf, field);
5418 if (buf.Length == 0)
5421 if (declType.IsEnum)
5424 if (field.IsStatic && !field.IsLiteral)
5425 buf.Append (" static");
5426 if (field.IsInitOnly)
5427 buf.Append (" readonly");
5428 if (field.IsLiteral)
5429 buf.Append (" const");
5431 buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
5432 buf.Append (field.Name);
5433 AppendFieldValue (buf, field);
5436 return buf.ToString ();
5439 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
5442 return buf.Append ("public");
5443 if (field.IsFamily || field.IsFamilyOrAssembly)
5444 return buf.Append ("protected");
5448 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
5450 // enums have a value__ field, which we ignore
5451 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
5452 field.DeclaringType.IsGenericType ())
5454 if (field.HasConstant && field.IsLiteral) {
5457 val = field.Constant;
5462 buf.Append (" = ").Append ("null");
5463 else if (val is Enum)
5464 buf.Append (" = ").Append (val.ToString ());
5465 else if (val is IFormattable) {
5466 string value = ((IFormattable)val).ToString();
5468 value = "\"" + value + "\"";
5469 buf.Append (" = ").Append (value);
5475 protected override string GetEventDeclaration (EventDefinition e)
5477 StringBuilder buf = new StringBuilder ();
5478 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
5482 AppendModifiers (buf, e.AddMethod);
5484 buf.Append (" event ");
5485 buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters [0]))).Append (' ');
5486 buf.Append (e.Name).Append (';');
5488 return buf.ToString ();
5492 class CSharpMemberFormatter : CSharpFullMemberFormatter {
5493 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5499 class DocTypeFullMemberFormatter : MemberFormatter {
5500 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
5502 protected override char NestedTypeSeparator {
5507 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
5508 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5514 class SlashDocMemberFormatter : MemberFormatter {
5516 protected override char[] GenericTypeContainer {
5517 get {return new char[]{'{', '}'};}
5520 private bool AddTypeCount = true;
5522 private TypeReference genDeclType;
5523 private MethodReference genDeclMethod;
5525 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
5527 if (type is GenericParameter) {
5529 if (genDeclType != null) {
5530 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
5531 for (int i = 0; i < genArgs.Count; ++i) {
5532 if (genArgs [i].Name == type.Name) {
5533 buf.Append ('`').Append (i);
5538 if (genDeclMethod != null) {
5539 IList<GenericParameter> genArgs = null;
5540 if (genDeclMethod.IsGenericMethod ()) {
5541 genArgs = genDeclMethod.GenericParameters;
5542 for (int i = 0; i < genArgs.Count; ++i) {
5543 if (genArgs [i].Name == type.Name) {
5544 buf.Append ("``").Append (i);
5550 if (genDeclType == null && genDeclMethod == null) {
5551 // Probably from within an explicitly implemented interface member,
5552 // where CSC uses parameter names instead of indices (why?), e.g.
5553 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
5554 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
5555 buf.Append (type.Name);
5557 if (buf.Length == l) {
5558 throw new Exception (string.Format (
5559 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
5560 type.Name, genDeclType, genDeclMethod));
5564 base.AppendTypeName (buf, type, context);
5566 int numArgs = type.GenericParameters.Count;
5567 if (type.DeclaringType != null)
5568 numArgs -= type.GenericParameters.Count;
5570 buf.Append ('`').Append (numArgs);
5577 protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
5579 buf.Append (ArrayDelimeters [0]);
5580 int rank = array.Rank;
5583 for (int i = 1; i < rank; ++i) {
5587 return buf.Append (ArrayDelimeters [1]);
5590 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5593 base.AppendGenericType (buf, type, context);
5595 AppendType (buf, type, context);
5599 private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5601 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
5602 bool insertNested = false;
5603 int prevParamCount = 0;
5604 foreach (var decl in decls) {
5606 buf.Append (NestedTypeSeparator);
5607 insertNested = true;
5608 base.AppendTypeName (buf, decl, context);
5609 int argCount = DocUtils.GetGenericArgumentCount (decl);
5610 int numArgs = argCount - prevParamCount;
5611 prevParamCount = argCount;
5613 buf.Append ('`').Append (numArgs);
5618 public override string GetDeclaration (MemberReference member)
5620 TypeReference r = member as TypeReference;
5622 return "T:" + GetTypeName (r);
5624 return base.GetDeclaration (member);
5627 protected override string GetConstructorName (MethodReference constructor)
5629 return GetMethodDefinitionName (constructor, "#ctor");
5632 protected override string GetMethodName (MethodReference method)
5635 MethodDefinition methodDef = method as MethodDefinition;
5636 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
5639 TypeReference iface;
5640 MethodReference ifaceMethod;
5641 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
5642 AddTypeCount = false;
5643 name = GetTypeName (iface) + "." + ifaceMethod.Name;
5644 AddTypeCount = true;
5646 return GetMethodDefinitionName (method, name);
5649 private string GetMethodDefinitionName (MethodReference method, string name)
5651 StringBuilder buf = new StringBuilder ();
5652 buf.Append (GetTypeName (method.DeclaringType));
5654 buf.Append (name.Replace (".", "#"));
5655 if (method.IsGenericMethod ()) {
5656 IList<GenericParameter> genArgs = method.GenericParameters;
5657 if (genArgs.Count > 0)
5658 buf.Append ("``").Append (genArgs.Count);
5660 IList<ParameterDefinition> parameters = method.Parameters;
5662 genDeclType = method.DeclaringType;
5663 genDeclMethod = method;
5664 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
5668 genDeclMethod = null;
5670 return buf.ToString ();
5673 private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
5675 if (parameters.Count == 0)
5680 AppendParameter (buf, genArgs, parameters [0]);
5681 for (int i = 1; i < parameters.Count; ++i) {
5683 AppendParameter (buf, genArgs, parameters [i]);
5686 return buf.Append (')');
5689 private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
5691 AddTypeCount = false;
5692 buf.Append (GetTypeName (parameter.ParameterType));
5693 AddTypeCount = true;
5697 protected override string GetPropertyName (PropertyReference property)
5701 PropertyDefinition propertyDef = property as PropertyDefinition;
5702 MethodDefinition method = null;
5703 if (propertyDef != null)
5704 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
5705 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
5706 name = property.Name;
5708 TypeReference iface;
5709 MethodReference ifaceMethod;
5710 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5711 AddTypeCount = false;
5712 name = string.Join ("#", new string[]{
5713 GetTypeName (iface).Replace (".", "#"),
5714 DocUtils.GetMember (property.Name)
5716 AddTypeCount = true;
5719 StringBuilder buf = new StringBuilder ();
5720 buf.Append (GetName (property.DeclaringType));
5723 IList<ParameterDefinition> parameters = property.Parameters;
5724 if (parameters.Count > 0) {
5725 genDeclType = property.DeclaringType;
5727 IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
5728 AppendParameter (buf, genArgs, parameters [0]);
5729 for (int i = 1; i < parameters.Count; ++i) {
5731 AppendParameter (buf, genArgs, parameters [i]);
5736 return buf.ToString ();
5739 protected override string GetFieldName (FieldReference field)
5741 return string.Format ("{0}.{1}",
5742 GetName (field.DeclaringType), field.Name);
5745 protected override string GetEventName (EventReference e)
5747 return string.Format ("{0}.{1}",
5748 GetName (e.DeclaringType), e.Name);
5751 protected override string GetTypeDeclaration (TypeDefinition type)
5753 string name = GetName (type);
5759 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5761 string name = GetName (constructor);
5767 protected override string GetMethodDeclaration (MethodDefinition method)
5769 string name = GetName (method);
5772 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
5773 genDeclType = method.DeclaringType;
5774 genDeclMethod = method;
5775 name += "~" + GetName (method.ReturnType);
5777 genDeclMethod = null;
5782 protected override string GetPropertyDeclaration (PropertyDefinition property)
5784 string name = GetName (property);
5790 protected override string GetFieldDeclaration (FieldDefinition field)
5792 string name = GetName (field);
5798 protected override string GetEventDeclaration (EventDefinition e)
5800 string name = GetName (e);
5807 class FileNameMemberFormatter : SlashDocMemberFormatter {
5808 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5813 protected override char NestedTypeSeparator {
5818 class ResolvedTypeInfo {
5819 TypeDefinition typeDef;
5821 public ResolvedTypeInfo (TypeReference value) {
5825 public TypeReference Reference { get; private set; }
5827 public TypeDefinition Definition {
5829 if (typeDef == null) {
5830 typeDef = Reference.Resolve ();
5837 /// <summary>Formats attribute values. Should return true if it is able to format the value.</summary>
5838 class AttributeValueFormatter {
5839 public virtual bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5841 TypeReference valueType = type.Reference;
5843 returnvalue = "null";
5846 if (valueType.FullName == "System.Type") {
5847 var vTypeRef = v as TypeReference;
5848 if (vTypeRef != null)
5849 returnvalue = "typeof(" + NativeTypeManager.GetTranslatedName (vTypeRef) + ")"; // TODO: drop NS handling
5851 returnvalue = "typeof(" + v.ToString () + ")";
5855 if (valueType.FullName == "System.String") {
5856 returnvalue = "\"" + v.ToString () + "\"";
5859 if (valueType.FullName == "System.Char") {
5860 returnvalue = "'" + v.ToString () + "'";
5864 returnvalue = (bool)v ? "true" : "false";
5868 TypeDefinition valueDef = type.Definition;
5869 if (valueDef == null || !valueDef.IsEnum) {
5870 returnvalue = v.ToString ();
5874 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5875 var values = MDocUpdater.GetEnumerationValues (valueDef);
5876 long c = MDocUpdater.ToInt64 (v);
5877 if (values.ContainsKey (c)) {
5878 returnvalue = typename + "." + values [c];
5887 /// <summary>The final value formatter in the pipeline ... if no other formatter formats the value,
5888 /// then this one will serve as the default implementation.</summary>
5889 class DefaultAttributeValueFormatter : AttributeValueFormatter {
5890 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5892 returnvalue = "(" + MDocUpdater.GetDocTypeFullName (type.Reference) + ") " + v.ToString ();
5897 /// <summary>Flags enum formatter that assumes powers of two values.</summary>
5898 /// <remarks>As described here: https://msdn.microsoft.com/en-us/library/vstudio/ms229062(v=vs.100).aspx</remarks>
5899 class StandardFlagsEnumFormatter : AttributeValueFormatter {
5900 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5902 TypeReference valueType = type.Reference;
5903 TypeDefinition valueDef = type.Definition;
5904 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
5906 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5907 var values = MDocUpdater.GetEnumerationValues (valueDef);
5908 long c = MDocUpdater.ToInt64 (v);
5909 returnvalue = string.Join (" | ",
5910 (from i in values.Keys
5911 where (c & i) == i && i != 0
5912 select typename + "." + values [i])
5913 .DefaultIfEmpty (c.ToString ()).ToArray ());
5923 /// <summary>A custom formatter for the ObjCRuntime.Platform enumeration.</summary>
5924 class ApplePlatformEnumFormatter : AttributeValueFormatter {
5925 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5927 TypeReference valueType = type.Reference;
5928 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5929 TypeDefinition valueDef = type.Definition;
5930 if (typename.Contains ("ObjCRuntime.Platform") && valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
5932 var values = MDocUpdater.GetEnumerationValues (valueDef);
5933 long c = MDocUpdater.ToInt64 (v);
5935 returnvalue = Format (c, values, typename);
5943 string Format (long c, IDictionary<long, string> values, string typename)
5945 int iosarch, iosmajor, iosminor, iossubminor;
5946 int macarch, macmajor, macminor, macsubminor;
5947 GetEncodingiOS (c, out iosarch, out iosmajor, out iosminor, out iossubminor);
5948 GetEncodingMac ((ulong)c, out macarch, out macmajor, out macminor, out macsubminor);
5950 if (iosmajor == 0 & iosminor == 0 && iossubminor == 0) {
5951 return FormatValues ("Mac", macarch, macmajor, macminor, macsubminor);
5954 if (macmajor == 0 & macminor == 0 && macsubminor == 0) {
5955 return FormatValues ("iOS", iosarch, iosmajor, iosminor, iossubminor);
5958 return string.Format ("(Platform){0}", c);
5961 string FormatValues (string plat, int arch, int major, int minor, int subminor)
5963 string archstring = "";
5972 return string.Format ("Platform.{4}_{0}_{1}{2} | Platform.{4}_Arch{3}",
5975 subminor == 0 ? "" : "_" + subminor.ToString (),
5981 void GetEncodingiOS (long entireLong, out int archindex, out int major, out int minor, out int subminor)
5983 long lowerBits = entireLong & 0xffffffff;
5984 int lowerBitsAsInt = (int) lowerBits;
5985 GetEncoding (lowerBitsAsInt, out archindex, out major, out minor, out subminor);
5988 void GetEncodingMac (ulong entireLong, out int archindex, out int major, out int minor, out int subminor)
5990 ulong higherBits = entireLong & 0xffffffff00000000;
5991 int higherBitsAsInt = (int) ((higherBits) >> 32);
5992 GetEncoding (higherBitsAsInt, out archindex, out major, out minor, out subminor);
5995 void GetEncoding (Int32 encodedBits, out int archindex, out int major, out int minor, out int subminor)
5997 // format is AAJJNNSS
5998 archindex = (int)((encodedBits & 0xFF000000) >> 24);
5999 major = (int)((encodedBits & 0x00FF0000) >> 16);
6000 minor = (int)((encodedBits & 0x0000FF00) >> 8);
6001 subminor = (int)((encodedBits & 0x000000FF) >> 0);