1 // Updater program for syncing Mono's ECMA-style documentation files
3 // By Joshua Tauberer <tauberer@for.net>
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Collections.ObjectModel;
9 using System.Diagnostics;
10 using System.Globalization;
15 using System.Xml.XPath;
20 using MyXmlNodeList = System.Collections.Generic.List<System.Xml.XmlNode>;
21 using StringList = System.Collections.Generic.List<string>;
22 using StringToStringMap = System.Collections.Generic.Dictionary<string, string>;
23 using StringToXmlNodeMap = System.Collections.Generic.Dictionary<string, System.Xml.XmlNode>;
25 namespace Mono.Documentation {
26 static class NativeTypeManager {
28 static Dictionary<string, string> toNativeType = new Dictionary<string,string>(){
32 {"System.Int32", "System.nint"},
35 {"System.UInt32", "System.nuint"},
38 {"System.Single", "System.nfloat"},
39 {"SizeF", "CoreGraphics.CGSize"},
40 {"System.Drawing.SizeF", "CoreGraphics.CGSize"},
41 {"PointF", "CoreGraphics.CGPoint"},
42 {"System.Drawing.PointF", "CoreGraphics.CGPoint"},
43 {"RectangleF", "CoreGraphics.CGRect" },
44 {"System.Drawing.RectangleF", "CoreGraphics.CGRect"}
47 static Dictionary<string, string> fromNativeType = new Dictionary<string,string>(){
50 {"System.nint", "System.Int32"},
52 {"System.nuint", "System.UInt32"},
54 {"System.nfloat", "System.Single"},
55 {"CoreGraphics.CGSize", "System.Drawing.SizeF"},
56 {"CoreGraphics.CGPoint", "System.Drawing.PointF"},
57 {"CoreGraphics.CGRect", "System.Drawing.RectangleF"},
58 {"MonoTouch.CoreGraphics.CGSize", "System.Drawing.SizeF"},
59 {"MonoTouch.CoreGraphics.CGPoint", "System.Drawing.PointF"},
60 {"MonoTouch.CoreGraphics.CGRect", "System.Drawing.RectangleF"}
63 public static string ConvertToNativeType(string typename) {
68 string valueToCompare = StripToComparableType (typename, ref isOut, ref isArray);
70 if (toNativeType.TryGetValue (valueToCompare, out nvalue)) {
82 public static string ConvertFromNativeType(string typename) {
87 string valueToCompare = StripToComparableType (typename, ref isOut, ref isArray);
89 if (fromNativeType.TryGetValue (valueToCompare, out nvalue)) {
98 // it wasn't one of the native types ... just return it
102 static string StripToComparableType (string typename, ref bool isOut, ref bool isArray)
104 string valueToCompare = typename;
105 if (typename.EndsWith ("[]")) {
106 valueToCompare = typename.Substring (0, typename.Length - 2);
109 if (typename.EndsWith ("&")) {
110 valueToCompare = typename.Substring (0, typename.Length - 1);
113 if (typename.Contains ("<")) {
114 // TODO: Need to recursively process generic parameters
116 return valueToCompare;
119 public static string GetTranslatedName(TypeReference t) {
120 string typename = t.FullName;
122 bool isInAssembly = MDocUpdater.IsInAssemblies (t.Module.Name);
123 if (isInAssembly && !typename.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (t)) {
124 string nameWithDropped = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typename);
125 return nameWithDropped;
130 class MDocUpdater : MDocCommand
133 List<AssemblyDefinition> assemblies;
134 readonly DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver();
138 bool show_exceptions;
139 bool no_assembly_versions, ignore_missing_types;
140 ExceptionLocations? exceptions;
142 internal int additions = 0, deletions = 0;
144 List<DocumentationImporter> importers = new List<DocumentationImporter> ();
146 DocumentationEnumerator docEnum;
150 static readonly MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
151 static readonly MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
153 static MemberFormatter[] typeFormatters = new MemberFormatter[]{
154 new CSharpMemberFormatter (),
155 new ILMemberFormatter (),
158 static MemberFormatter[] memberFormatters = new MemberFormatter[]{
159 new CSharpFullMemberFormatter (),
160 new ILFullMemberFormatter (),
163 internal static readonly MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
165 MyXmlNodeList extensionMethods = new MyXmlNodeList ();
167 HashSet<string> forwardedTypes = new HashSet<string> ();
169 public static string droppedNamespace = string.Empty;
171 public static bool HasDroppedNamespace(TypeDefinition forType)
173 return HasDroppedNamespace(forType.Module);
176 public static bool HasDroppedNamespace(MemberReference forMember)
178 return HasDroppedNamespace(forMember.Module);
181 public static bool HasDroppedNamespace(AssemblyDefinition forAssembly)
183 return HasDroppedNamespace(forAssembly.MainModule);
186 public static bool HasDroppedNamespace(ModuleDefinition forModule)
188 return !string.IsNullOrWhiteSpace (droppedNamespace) && droppedAssemblies.Any(da => da == forModule.Name);
191 public static bool HasDroppedAnyNamespace ()
193 return !string.IsNullOrWhiteSpace (droppedNamespace);
197 static List<string> droppedAssemblies = new List<string>();
199 public string PreserveTag { get; set; }
200 public static MDocUpdater Instance { get; private set; }
201 public static bool SwitchingToMagicTypes { get; private set; }
203 public override void Run (IEnumerable<string> args)
206 show_exceptions = DebugOutput;
207 var types = new List<string> ();
208 var p = new OptionSet () {
210 "Delete removed members from the XML files.",
211 v => delete = v != null },
213 "Document potential exceptions that members can generate. {SOURCES} " +
214 "is a comma-separated list of:\n" +
215 " asm Method calls in same assembly\n" +
216 " depasm Method calls in dependent assemblies\n" +
217 " all Record all possible exceptions\n" +
218 " added Modifier; only create <exception/>s\n" +
219 " for NEW types/members\n" +
220 "If nothing is specified, then only exceptions from the member will " +
222 v => exceptions = ParseExceptionLocations (v) },
224 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
227 case "ignore-missing-types":
228 ignore_missing_types = true;
230 case "no-assembly-versions":
231 no_assembly_versions = true;
234 throw new Exception ("Unsupported flag `" + v + "'.");
237 { "fignore-missing-types",
238 "Do not report an error if a --type=TYPE type\nwas not found.",
239 v => ignore_missing_types = v != null },
240 { "fno-assembly-versions",
241 "Do not generate //AssemblyVersion elements.",
242 v => no_assembly_versions = v != null },
244 "Import documentation from {FILE}.",
245 v => AddImporter (v) },
247 "Check for assembly references in {DIRECTORY}.",
248 v => assemblyResolver.AddSearchDirectory (v) },
250 "Ignored for compatibility with update-ecma-xml.",
253 "Root {DIRECTORY} to generate/update documentation.",
256 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
257 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
258 v => assemblyResolver.AddSearchDirectory (Path.GetDirectoryName (v)) },
260 "Manually specify the assembly {VERSION} that new members were added in.",
263 "Only update documentation for {TYPE}.",
264 v => types.Add (v) },
266 "When processing assembly {ASSEMBLY}, strip off leading namespace {PREFIX}:\n" +
267 " e.g. --dropns ASSEMBLY=PREFIX",
269 var parts = v.Split ('=');
270 if (parts.Length != 2) { Console.Error.WriteLine ("Invalid dropns input"); return; }
271 var assembly = Path.GetFileName (parts [0].Trim ());
272 var prefix = parts [1].Trim();
273 droppedAssemblies.Add (assembly);
274 droppedNamespace = prefix;
277 "If the new assembly is switching to 'magic types', then this switch should be defined.",
278 v => SwitchingToMagicTypes = true },
280 "Do not delete members that don't exist in the assembly, but rather mark them as preserved.",
281 v => PreserveTag = "true" },
283 "Allow types to be in multiple assemblies.",
284 v => multiassembly = true },
286 var assemblies = Parse (p, args, "update",
287 "[OPTIONS]+ ASSEMBLIES",
288 "Create or update documentation from ASSEMBLIES.");
289 if (assemblies == null)
291 if (assemblies.Count == 0)
292 Error ("No assemblies specified.");
294 foreach (var dir in assemblies
295 .Where (a => a.Contains (Path.DirectorySeparatorChar))
296 .Select (a => Path.GetDirectoryName (a)))
297 assemblyResolver.AddSearchDirectory (dir);
299 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
302 throw new InvalidOperationException("The --out option is required.");
304 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
306 // Store types that have been forwarded to avoid duplicate generation
307 GatherForwardedTypes ();
309 docEnum = docEnum ?? new DocumentationEnumerator ();
311 // PERFORM THE UPDATES
313 if (types.Count > 0) {
315 DoUpdateTypes (srcPath, types, srcPath);
318 else if (opts.@namespace != null)
319 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
320 Path.Combine (dest_dir, opts.@namespace));
323 DoUpdateAssemblies (srcPath, srcPath);
325 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
327 public static bool IsInAssemblies(string name) {
328 var query = Instance.assemblies.Where (a => a.MainModule.Name == name).ToArray ();
329 return query.Length > 0;
331 void AddImporter (string path)
334 XmlReader r = new XmlTextReader (path);
336 while (r.NodeType != XmlNodeType.Element) {
338 Error ("Unable to read XML file: {0}.", path);
340 if (r.LocalName == "doc") {
341 importers.Add (new MsxdocDocumentationImporter (path));
343 else if (r.LocalName == "Libraries") {
344 var ecmadocs = new XmlTextReader (path);
345 docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
346 importers.Add (new EcmaDocumentationImporter (ecmadocs));
349 Error ("Unsupported XML format within {0}.", path);
352 } catch (Exception e) {
353 Environment.ExitCode = 1;
354 Error ("Could not load XML file: {0}.", e.Message);
358 void GatherForwardedTypes ()
360 foreach (var asm in assemblies)
361 foreach (var type in asm.MainModule.ExportedTypes.Where (t => t.IsForwarder).Select (t => t.FullName))
362 forwardedTypes.Add (type);
365 static ExceptionLocations ParseExceptionLocations (string s)
367 ExceptionLocations loc = ExceptionLocations.Member;
370 foreach (var type in s.Split (',')) {
372 case "added": loc |= ExceptionLocations.AddedMembers; break;
373 case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
374 case "asm": loc |= ExceptionLocations.Assembly; break;
375 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
376 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
382 internal void Warning (string format, params object[] args)
384 Message (TraceLevel.Warning, "mdoc: " + format, args);
387 private AssemblyDefinition LoadAssembly (string name)
389 AssemblyDefinition assembly = null;
391 assembly = AssemblyDefinition.ReadAssembly (name, new ReaderParameters { AssemblyResolver = assemblyResolver });
392 } catch (System.IO.FileNotFoundException) { }
394 if (assembly == null)
395 throw new InvalidOperationException("Assembly " + name + " not found.");
400 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
401 OrderTypeAttributes (element);
402 XmlTextWriter writer = new XmlTextWriter(output);
403 writer.Formatting = Formatting.Indented;
404 writer.Indentation = 2;
405 writer.IndentChar = ' ';
406 element.WriteTo(writer);
410 private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
412 Action<string> creator = file => {
413 using (var writer = OpenWrite (file, mode))
417 MdocFile.UpdateFile (filename, creator);
420 private static void OrderTypeAttributes (XmlElement e)
422 foreach (XmlElement type in e.SelectNodes ("//Type")) {
423 OrderTypeAttributes (type.Attributes);
427 static readonly string[] TypeAttributeOrder = {
428 "Name", "FullName", "FullNameSP", "Maintainer"
431 private static void OrderTypeAttributes (XmlAttributeCollection c)
433 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
434 for (int i = 0; i < c.Count; ++i) {
435 XmlAttribute a = c [i];
436 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
437 if (a.Name == TypeAttributeOrder [j]) {
443 for (int i = attrs.Length-1; i >= 0; --i) {
444 XmlAttribute n = attrs [i];
447 XmlAttribute r = null;
448 for (int j = i+1; j < attrs.Length; ++j) {
449 if (attrs [j] != null) {
456 if (c [n.Name] != null) {
457 c.RemoveNamedItem (n.Name);
458 c.InsertBefore (n, r);
463 private XmlDocument CreateIndexStub()
465 XmlDocument index = new XmlDocument();
467 XmlElement index_root = index.CreateElement("Overview");
468 index.AppendChild(index_root);
470 if (assemblies.Count == 0)
471 throw new Exception ("No assembly");
473 XmlElement index_assemblies = index.CreateElement("Assemblies");
474 index_root.AppendChild(index_assemblies);
476 XmlElement index_remarks = index.CreateElement("Remarks");
477 index_remarks.InnerText = "To be added.";
478 index_root.AppendChild(index_remarks);
480 XmlElement index_copyright = index.CreateElement("Copyright");
481 index_copyright.InnerText = "To be added.";
482 index_root.AppendChild(index_copyright);
484 XmlElement index_types = index.CreateElement("Types");
485 index_root.AppendChild(index_types);
490 private static void WriteNamespaceStub(string ns, string outdir) {
491 XmlDocument index = new XmlDocument();
493 XmlElement index_root = index.CreateElement("Namespace");
494 index.AppendChild(index_root);
496 index_root.SetAttribute("Name", ns);
498 XmlElement index_docs = index.CreateElement("Docs");
499 index_root.AppendChild(index_docs);
501 XmlElement index_summary = index.CreateElement("summary");
502 index_summary.InnerText = "To be added.";
503 index_docs.AppendChild(index_summary);
505 XmlElement index_remarks = index.CreateElement("remarks");
506 index_remarks.InnerText = "To be added.";
507 index_docs.AppendChild(index_remarks);
509 WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
510 writer => WriteXml (index.DocumentElement, writer));
513 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
515 var index = CreateIndexForTypes (dest);
517 var found = new HashSet<string> ();
518 foreach (AssemblyDefinition assembly in assemblies) {
519 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames)) {
520 string relpath = DoUpdateType (type, basepath, dest);
524 found.Add (type.FullName);
529 index.Add (assembly);
537 if (ignore_missing_types)
540 var notFound = from n in typenames where !found.Contains (n) select n;
542 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
545 class IndexForTypes {
551 XmlElement index_types;
552 XmlElement index_assemblies;
554 public IndexForTypes (MDocUpdater app, string indexFile, XmlDocument index)
557 this.indexFile = indexFile;
560 index_types = WriteElement (index.DocumentElement, "Types");
561 index_assemblies = WriteElement (index.DocumentElement, "Assemblies");
564 public void Add (AssemblyDefinition assembly)
566 if (index_assemblies.SelectSingleNode ("Assembly[@Name='" + assembly.Name.Name + "']") != null)
569 app.AddIndexAssembly (assembly, index_assemblies);
572 public void Add (TypeDefinition type)
574 app.AddIndexType (type, index_types);
579 SortIndexEntries (index_types);
580 WriteFile (indexFile, FileMode.Create,
581 writer => WriteXml (index.DocumentElement, writer));
585 IndexForTypes CreateIndexForTypes (string dest)
587 string indexFile = Path.Combine (dest, "index.xml");
588 if (File.Exists (indexFile))
590 return new IndexForTypes (this, indexFile, CreateIndexStub ());
593 /// <summary>Constructs the presumed path to the type's documentation file</summary>
594 /// <returns><c>true</c>, if the type file was found, <c>false</c> otherwise.</returns>
595 /// <param name="result">A typle that contains 1) the 'reltypefile', 2) the 'typefile', and 3) the file info</param>
596 bool TryFindTypeFile(string nsname, string typename, string basepath, out Tuple<string, string, FileInfo> result) {
597 string reltypefile = DocUtils.PathCombine (nsname, typename + ".xml");
598 string typefile = Path.Combine (basepath, reltypefile);
599 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
601 result = new Tuple<string, string, FileInfo> (reltypefile, typefile, file);
606 public string DoUpdateType (TypeDefinition type, string basepath, string dest)
608 if (type.Namespace == null)
609 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
611 if (!IsPublic (type))
614 // Must get the A+B form of the type name.
615 string typename = GetTypeFileName(type);
616 string nsname = DocUtils.GetNamespace (type);
618 // Find the file, if it exists
619 string[] searchLocations = new string[] {
623 if (MDocUpdater.HasDroppedNamespace (type)) {
624 // If dropping namespace, types may have moved into a couple of different places.
625 var newSearchLocations = searchLocations.Union (new string[] {
626 string.Format ("{0}.{1}", droppedNamespace, nsname),
627 nsname.Replace (droppedNamespace + ".", string.Empty),
628 MDocUpdater.droppedNamespace
631 searchLocations = newSearchLocations.ToArray ();
634 string reltypefile="", typefile="";
635 System.IO.FileInfo file = null;
637 foreach (var f in searchLocations) {
638 Tuple<string, string, FileInfo> result;
639 bool fileExists = TryFindTypeFile (f, typename, basepath, out result);
642 reltypefile = result.Item1;
643 typefile = result.Item2;
650 if (file == null || !file.Exists) {
651 // we were not able to find a file, let's use the original type informatio.
652 // so that we create the stub in the right place.
653 Tuple<string, string, FileInfo> result;
654 TryFindTypeFile (nsname, typename, basepath, out result);
656 reltypefile = result.Item1;
657 typefile = result.Item2;
661 string output = null;
664 } else if (dest == "-") {
667 output = Path.Combine (dest, reltypefile);
670 if (file != null && file.Exists) {
672 XmlDocument basefile = new XmlDocument();
674 basefile.Load(typefile);
675 } catch (Exception e) {
676 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
679 DoUpdateType2("Updating", basefile, type, output, false);
682 XmlElement td = StubType(type, output);
689 public void DoUpdateNS (string ns, string nspath, string outpath)
691 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
692 AssemblyDefinition assembly = assemblies [0];
694 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
695 XmlDocument basefile = new XmlDocument();
696 string typefile = Path.Combine(nspath, file.Name);
698 basefile.Load(typefile);
699 } catch (Exception e) {
700 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
704 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
705 TypeDefinition type = assembly.GetType(typename);
708 if (!string.IsNullOrWhiteSpace (droppedNamespace)) {
709 string nameWithNs = string.Format ("{0}.{1}", droppedNamespace, typename);
710 type = assembly.GetType (nameWithNs);
712 Warning ("Type no longer in assembly: " + typename);
719 seenTypes[type] = seenTypes;
720 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false);
723 // Stub types not in the directory
724 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
725 if (type.Namespace != ns || seenTypes.ContainsKey(type))
728 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"));
729 if (td == null) continue;
733 private static string GetTypeFileName (TypeReference type)
735 return filenameFormatter.GetName (type);
738 public static string GetTypeFileName (string typename)
740 StringBuilder filename = new StringBuilder (typename.Length);
744 for (int i = 0; i < typename.Length; ++i) {
745 char c = typename [i];
754 filename.Append ('`').Append ((numArgs+1).ToString());
769 return filename.ToString ();
772 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
774 XmlElement index_assembly = null;
776 index_assembly = (XmlElement)parent.SelectSingleNode ("Assembly[@Name='"+ assembly.Name.Name +"']");
778 if (index_assembly == null)
779 index_assembly = parent.OwnerDocument.CreateElement ("Assembly");
781 index_assembly.SetAttribute ("Name", assembly.Name.Name);
782 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
784 AssemblyNameDefinition name = assembly.Name;
785 if (name.HasPublicKey) {
786 XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
787 var key = new StringBuilder (name.PublicKey.Length*3 + 2);
789 foreach (byte b in name.PublicKey)
790 key.AppendFormat ("{0,2:x2} ", b);
792 pubkey.InnerText = key.ToString ();
793 index_assembly.AppendChild (pubkey);
796 if (!string.IsNullOrEmpty (name.Culture)) {
797 XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
798 culture.InnerText = name.Culture;
799 index_assembly.AppendChild (culture);
802 MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
803 parent.AppendChild(index_assembly);
806 private void AddIndexType (TypeDefinition type, XmlElement index_types)
808 string typename = GetTypeFileName(type);
810 // Add namespace and type nodes into the index file as needed
811 string ns = DocUtils.GetNamespace (type);
812 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode ("Namespace[@Name='" + ns + "']");
813 if (nsnode == null) {
814 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
815 nsnode.SetAttribute ("Name", ns);
816 index_types.AppendChild (nsnode);
818 string doc_typename = GetDocTypeName (type);
819 XmlElement typenode = (XmlElement) nsnode.SelectSingleNode ("Type[@Name='" + typename + "']");
820 if (typenode == null) {
821 typenode = index_types.OwnerDocument.CreateElement ("Type");
822 typenode.SetAttribute ("Name", typename);
823 nsnode.AppendChild (typenode);
825 if (typename != doc_typename)
826 typenode.SetAttribute("DisplayName", doc_typename);
828 typenode.RemoveAttribute("DisplayName");
830 typenode.SetAttribute ("Kind", GetTypeKind (type));
833 private void DoUpdateAssemblies (string source, string dest)
835 string indexfile = dest + "/index.xml";
837 if (System.IO.File.Exists(indexfile)) {
838 index = new XmlDocument();
839 index.Load(indexfile);
842 ClearElement(index.DocumentElement, "Assembly");
843 ClearElement(index.DocumentElement, "Attributes");
845 index = CreateIndexStub();
848 string defaultTitle = "Untitled";
849 if (assemblies.Count == 1)
850 defaultTitle = assemblies[0].Name.Name;
851 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
853 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
854 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
856 index_assemblies.RemoveAll ();
859 HashSet<string> goodfiles = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
861 foreach (AssemblyDefinition assm in assemblies) {
862 AddIndexAssembly (assm, index_assemblies);
863 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
866 SortIndexEntries (index_types);
868 CleanupFiles (dest, goodfiles);
869 CleanupIndexTypes (index_types, goodfiles);
870 CleanupExtensions (index_types);
872 WriteFile (indexfile, FileMode.Create,
873 writer => WriteXml(index.DocumentElement, writer));
876 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
878 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
880 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
881 string typename = GetTypeFileName(type);
882 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0 || forwardedTypes.Contains (type.FullName))
885 string reltypepath = DoUpdateType (type, source, dest);
886 if (reltypepath == null)
889 // Add namespace and type nodes into the index file as needed
890 AddIndexType (type, index_types);
892 // Ensure the namespace index file exists
893 string namespaceToUse = type.Namespace;
894 if (HasDroppedNamespace(assembly)) {
895 namespaceToUse = string.Format ("{0}.{1}", droppedNamespace, namespaceToUse);
897 string onsdoc = DocUtils.PathCombine (dest, namespaceToUse + ".xml");
898 string nsdoc = DocUtils.PathCombine (dest, "ns-" + namespaceToUse + ".xml");
899 if (File.Exists (onsdoc)) {
900 File.Move (onsdoc, nsdoc);
903 if (!File.Exists (nsdoc)) {
904 Console.WriteLine("New Namespace File: " + type.Namespace);
905 WriteNamespaceStub(namespaceToUse, dest);
908 goodfiles.Add (reltypepath);
912 private static void SortIndexEntries (XmlElement indexTypes)
914 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
915 XmlNodeComparer c = new AttributeNameComparer ();
916 SortXmlNodes (indexTypes, namespaces, c);
918 for (int i = 0; i < namespaces.Count; ++i)
919 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
922 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
924 MyXmlNodeList l = new MyXmlNodeList (children.Count);
925 for (int i = 0; i < children.Count; ++i)
926 l.Add (children [i]);
928 for (int i = l.Count - 1; i > 0; --i) {
929 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
933 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
935 public abstract int Compare (XmlNode x, XmlNode y);
937 public int Compare (object x, object y)
939 return Compare ((XmlNode) x, (XmlNode) y);
943 class AttributeNameComparer : XmlNodeComparer {
946 public AttributeNameComparer ()
951 public AttributeNameComparer (string attribute)
953 this.attribute = attribute;
956 public override int Compare (XmlNode x, XmlNode y)
958 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
962 class VersionComparer : XmlNodeComparer {
963 public override int Compare (XmlNode x, XmlNode y)
965 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
966 string a = GetVersion (x.InnerText);
967 string b = GetVersion (y.InnerText);
968 return new Version (a).CompareTo (new Version (b));
971 static string GetVersion (string v)
973 int n = v.IndexOf ("x");
976 return v.Substring (0, n-1);
980 private static string GetTypeKind (TypeDefinition type)
983 return "Enumeration";
984 if (type.IsValueType)
986 if (type.IsInterface)
988 if (DocUtils.IsDelegate (type))
990 if (type.IsClass || type.FullName == "System.Enum") // FIXME
992 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
995 public static bool IsPublic (TypeDefinition type)
997 TypeDefinition decl = type;
998 while (decl != null) {
999 if (!(decl.IsPublic || decl.IsNestedPublic ||
1000 decl.IsNestedFamily || decl.IsNestedFamily || decl.IsNestedFamilyOrAssembly)) {
1003 decl = (TypeDefinition) decl.DeclaringType;
1008 private void CleanupFiles (string dest, HashSet<string> goodfiles)
1010 // Look for files that no longer correspond to types
1011 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
1012 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
1013 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
1014 if (!goodfiles.Contains (relTypeFile)) {
1015 XmlDocument doc = new XmlDocument ();
1016 doc.Load (typefile.FullName);
1017 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
1018 string assemblyName = doc.SelectSingleNode ("/Type/AssemblyInfo/AssemblyName").InnerText;
1019 AssemblyDefinition assembly = assemblies.FirstOrDefault (a => a.Name.Name == assemblyName);
1021 Action saveDoc = () => {
1022 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
1023 WriteXml(doc.DocumentElement, writer);
1026 if (e != null && !no_assembly_versions && assembly != null && assemblyName != null && UpdateAssemblyVersions (e, assembly, GetAssemblyVersions(assemblyName), false)) {
1028 goodfiles.Add (relTypeFile);
1032 Action actuallyDelete = () => {
1033 string newname = typefile.FullName + ".remove";
1034 try { System.IO.File.Delete (newname); } catch (Exception) { Warning ("Unable to delete existing file: {0}", newname); }
1035 try { typefile.MoveTo (newname); } catch (Exception) { Warning ("Unable to rename to: {0}", newname); }
1036 Console.WriteLine ("Class no longer present; file renamed: " + Path.Combine (nsdir.Name, typefile.Name));
1039 if (string.IsNullOrWhiteSpace (PreserveTag)) { // only do this if there was not a -preserve
1042 var unifiedAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[@apistyle='unified']");
1043 var classicAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[@apistyle='classic']");
1044 var unifiedMembers = doc.SelectNodes ("//Member[@apistyle='unified']|//Member/AssemblyInfo[@apistyle='unified']");
1045 var classicMembers = doc.SelectNodes ("//Member[@apistyle='classic']|//Member/AssemblyInfo[@apistyle='classic']");
1046 bool isUnifiedRun = HasDroppedAnyNamespace ();
1047 bool isClassicOrNormalRun = !isUnifiedRun;
1049 Action<XmlNode, ApiStyle> removeStyles = (x, style) => {
1050 var styledNodes = doc.SelectNodes("//*[@apistyle='"+ style.ToString ().ToLowerInvariant () +"']");
1051 if (styledNodes != null && styledNodes.Count > 0) {
1052 foreach(var node in styledNodes.Cast<XmlNode> ()) {
1053 node.ParentNode.RemoveChild (node);
1058 if (isClassicOrNormalRun) {
1059 if (unifiedAssemblyNode != null || unifiedMembers.Count > 0) {
1060 Warning ("*** this type is marked as unified, not deleting during this run: {0}", typefile.FullName);
1061 // if truly removed from both assemblies, it will be removed fully during the unified run
1062 removeStyles (doc, ApiStyle.Classic);
1065 // we should be safe to delete here because it was not marked as a unified assembly
1070 if (classicAssemblyNode != null || classicMembers.Count > 0) {
1071 Warning ("*** this type is marked as classic, not deleting {0}", typefile.FullName);
1074 // safe to delete because it wasn't marked as a classic assembly, so the type is gone in both.
1084 private static TextWriter OpenWrite (string path, FileMode mode)
1086 var w = new StreamWriter (
1087 new FileStream (path, mode),
1088 new UTF8Encoding (false)
1094 private string[] GetAssemblyVersions (string assemblyName)
1096 return (from a in assemblies
1097 where a.Name.Name == assemblyName
1098 select GetAssemblyVersion (a)).ToArray ();
1101 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
1103 // Look for type nodes that no longer correspond to types
1104 MyXmlNodeList remove = new MyXmlNodeList ();
1105 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
1106 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
1107 if (!goodfiles.Contains (fulltypename)) {
1108 remove.Add (typenode);
1111 foreach (XmlNode n in remove)
1112 n.ParentNode.RemoveChild (n);
1115 private void CleanupExtensions (XmlElement index_types)
1117 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
1118 if (extensionMethods.Count == 0) {
1121 index_types.SelectSingleNode ("/Overview").RemoveChild (e);
1125 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
1126 index_types.SelectSingleNode ("/Overview").AppendChild (e);
1130 extensionMethods.Sort (DefaultExtensionMethodComparer);
1131 foreach (XmlNode m in extensionMethods) {
1132 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
1136 class ExtensionMethodComparer : XmlNodeComparer {
1137 public override int Compare (XmlNode x, XmlNode y)
1139 XmlNode xLink = x.SelectSingleNode ("Member/Link");
1140 XmlNode yLink = y.SelectSingleNode ("Member/Link");
1142 int n = xLink.Attributes ["Type"].Value.CompareTo (
1143 yLink.Attributes ["Type"].Value);
1146 n = xLink.Attributes ["Member"].Value.CompareTo (
1147 yLink.Attributes ["Member"].Value);
1148 if (n == 0 && !object.ReferenceEquals (x, y))
1149 throw new InvalidOperationException ("Duplicate extension method found!");
1154 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
1156 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince)
1158 Console.WriteLine(message + ": " + type.FullName);
1160 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
1162 // Update type metadata
1163 UpdateType(basefile.DocumentElement, type);
1165 // Update existing members. Delete member nodes that no longer should be there,
1166 // and remember what members are already documented so we don't add them again.
1168 MyXmlNodeList todelete = new MyXmlNodeList ();
1170 foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
1171 XmlElement oldmember = info.Node;
1172 MemberReference oldmember2 = info.Member;
1174 if (info.Member != null && info.Node != null) {
1175 // Check for an error condition where the xml MemberName doesn't match the matched member
1176 var memberName = GetMemberName (info.Member);
1177 var memberAttribute = info.Node.Attributes ["MemberName"];
1178 if (memberAttribute == null || (memberAttribute.Value != memberName && memberAttribute.Value.Split (',').Length != memberName.Split (',').Length)) {
1179 oldmember.SetAttribute ("MemberName", memberName);
1183 string sig = oldmember2 != null ? memberFormatters [0].GetDeclaration (oldmember2) : null;
1185 // Interface implementations and overrides are deleted from the docs
1186 // unless the overrides option is given.
1187 if (oldmember2 != null && sig == null)
1190 // Deleted (or signature changed)
1191 if (oldmember2 == null) {
1192 if (!no_assembly_versions && UpdateAssemblyVersions (oldmember, type.Module.Assembly, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
1195 DeleteMember ("Member Removed", output, oldmember, todelete, type);
1200 if (seenmembers.ContainsKey (sig)) {
1201 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
1202 // ignore, already seen
1204 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
1205 DeleteMember ("Duplicate Member Found", output, oldmember, todelete, type);
1207 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
1211 // Update signature information
1214 // get all apistyles of sig from info.Node
1215 var styles = oldmember.GetElementsByTagName ("MemberSignature").Cast<XmlElement> ()
1216 .Where (x => x.GetAttribute ("Language") == "C#" && !seenmembers.ContainsKey(x.GetAttribute("Value")))
1217 .Select (x => x.GetAttribute ("Value"));
1219 foreach (var stylesig in styles) {
1220 seenmembers.Add (stylesig, oldmember);
1223 foreach (XmlElement oldmember in todelete)
1224 oldmember.ParentNode.RemoveChild (oldmember);
1227 if (!DocUtils.IsDelegate (type)) {
1228 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
1229 var typemembers = type.GetMembers()
1231 if (m is TypeDefinition) return false;
1232 string sig = memberFormatters [0].GetDeclaration (m);
1233 if (sig == null) return false;
1234 if (seenmembers.ContainsKey(sig)) return false;
1236 // Verify that the member isn't an explicitly implemented
1237 // member of an internal interface, in which case we shouldn't return true.
1238 MethodDefinition methdef = null;
1239 if (m is MethodDefinition)
1240 methdef = m as MethodDefinition;
1241 else if (m is PropertyDefinition) {
1242 var prop = m as PropertyDefinition;
1243 methdef = prop.GetMethod ?? prop.SetMethod;
1246 if (methdef != null) {
1247 TypeReference iface;
1248 MethodReference imethod;
1250 if (methdef.Overrides.Count == 1) {
1251 DocUtils.GetInfoForExplicitlyImplementedMethod (methdef, out iface, out imethod);
1252 if (!IsPublic (iface.Resolve ())) return false;
1259 foreach (MemberReference m in typemembers) {
1260 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
1261 if (mm == null) continue;
1263 if (MDocUpdater.SwitchingToMagicTypes) {
1264 // this is a unified style API that obviously doesn't exist in the classic API. Let's mark
1265 // it with apistyle="unified", so that it's not displayed for classic style APIs
1266 mm.SetAttribute ("apistyle", "unified");
1269 members.AppendChild( mm );
1271 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
1276 // Import code snippets from files
1277 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
1278 if (!(code is XmlElement)) continue;
1279 string file = ((XmlElement)code).GetAttribute("src");
1280 string lang = ((XmlElement)code).GetAttribute("lang");
1282 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
1284 code.InnerText = src;
1288 if (insertSince && since != null) {
1289 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
1290 docs.AppendChild (CreateSinceNode (basefile));
1294 XmlElement d = basefile.DocumentElement ["Docs"];
1295 XmlElement m = basefile.DocumentElement ["Members"];
1296 if (d != null && m != null)
1297 basefile.DocumentElement.InsertBefore (
1298 basefile.DocumentElement.RemoveChild (d), m);
1299 SortTypeMembers (m);
1303 WriteXml(basefile.DocumentElement, Console.Out);
1305 FileInfo file = new FileInfo (output);
1306 if (!file.Directory.Exists) {
1307 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
1308 file.Directory.Create ();
1310 WriteFile (output, FileMode.Create,
1311 writer => WriteXml(basefile.DocumentElement, writer));
1315 private string GetCodeSource (string lang, string file)
1318 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
1319 // Grab the specified region
1320 string region = "#region " + file.Substring (anchorStart + 4);
1321 file = file.Substring (0, anchorStart + 3);
1323 using (StreamReader reader = new StreamReader (file)) {
1325 StringBuilder src = new StringBuilder ();
1327 while ((line = reader.ReadLine ()) != null) {
1328 if (line.Trim() == region) {
1329 indent = line.IndexOf (region);
1332 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
1337 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
1340 return src.ToString ();
1342 } catch (Exception e) {
1343 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
1344 file, region, show_exceptions ? e.ToString () : e.Message);
1349 using (StreamReader reader = new StreamReader (file))
1350 return reader.ReadToEnd ();
1351 } catch (Exception e) {
1352 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
1357 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete, TypeDefinition type)
1359 string format = output != null
1360 ? "{0}: File='{1}'; Signature='{4}'"
1361 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1362 string signature = member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value;
1366 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1367 member.Attributes ["MemberName"].Value,
1370 // Identify all of the different states that could affect our decision to delete the member
1371 bool shouldPreserve = !string.IsNullOrWhiteSpace (PreserveTag);
1372 bool hasContent = MemberDocsHaveUserContent (member);
1373 bool shouldDelete = !shouldPreserve && (delete || !hasContent);
1375 bool unifiedRun = HasDroppedNamespace (type);
1377 var classicAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[@apistyle='classic']");
1378 bool nodeIsClassic = classicAssemblyInfo != null || member.HasApiStyle (ApiStyle.Classic);
1379 var unifiedAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']");
1380 bool nodeIsUnified = unifiedAssemblyInfo != null || member.HasApiStyle (ApiStyle.Unified);
1382 Action actuallyDelete = () => {
1383 todelete.Add (member);
1387 if (!shouldDelete) {
1388 // explicitly not deleting
1389 string message = shouldPreserve ?
1390 "Not deleting '{0}' due to --preserve." :
1391 "Not deleting '{0}'; must be enabled with the --delete option";
1392 Warning (message, signature);
1393 } else if (unifiedRun && nodeIsClassic) {
1394 // this is a unified run, and the member doesn't exist, but is marked as being in the classic assembly.
1395 member.RemoveApiStyle (ApiStyle.Unified);
1396 Warning ("Not removing '{0}' since it's still in the classic assembly.", signature);
1397 } else if (unifiedRun && !nodeIsClassic) {
1398 // unified run, and the node is not classic, which means it doesn't exist anywhere.
1401 if (!nodeIsClassic && !nodeIsUnified) { // regular codepath (ie. not classic/unified)
1403 } else { // this is a classic run
1404 Warning ("Removing classic from '{0}' ... will be removed in the unified run if not present there.", signature);
1405 member.RemoveApiStyle (ApiStyle.Classic);
1406 if (classicAssemblyInfo != null) {
1407 member.RemoveChild (classicAssemblyInfo);
1413 class MemberComparer : XmlNodeComparer {
1414 public override int Compare (XmlNode x, XmlNode y)
1417 string xMemberName = x.Attributes ["MemberName"].Value;
1418 string yMemberName = y.Attributes ["MemberName"].Value;
1420 // generic methods *end* with '>'
1421 // it's possible for explicitly implemented generic interfaces to
1422 // contain <...> without being a generic method
1423 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1424 (r = xMemberName.CompareTo (yMemberName)) != 0)
1428 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1429 xMemberName = xMemberName.Substring (0, lt);
1430 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1431 yMemberName = yMemberName.Substring (0, lt);
1432 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1435 // if @MemberName matches, then it's either two different types of
1436 // members sharing the same name, e.g. field & property, or it's an
1437 // overloaded method.
1438 // for different type, sort based on MemberType value.
1439 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1440 y.SelectSingleNode ("MemberType").InnerText);
1444 // same type -- must be an overloaded method. Sort based on type
1445 // parameter count, then parameter count, then by the parameter
1447 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1448 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1449 if (xTypeParams.Count != yTypeParams.Count)
1450 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1451 for (int i = 0; i < xTypeParams.Count; ++i) {
1452 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1453 yTypeParams [i].Attributes ["Name"].Value);
1458 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1459 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1460 if (xParams.Count != yParams.Count)
1461 return xParams.Count <= yParams.Count ? -1 : 1;
1462 for (int i = 0; i < xParams.Count; ++i) {
1463 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1464 yParams [i].Attributes ["Type"].Value);
1468 // all parameters match, but return value might not match if it was
1469 // changed between one version and another.
1470 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1471 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1472 if (xReturn != null && yReturn != null) {
1473 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1482 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1484 private static void SortTypeMembers (XmlNode members)
1486 if (members == null)
1488 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1491 private static bool MemberDocsHaveUserContent (XmlNode e)
1493 e = (XmlElement)e.SelectSingleNode("Docs");
1494 if (e == null) return false;
1495 foreach (XmlElement d in e.SelectNodes("*"))
1496 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1501 // UPDATE HELPER FUNCTIONS
1503 // CREATE A STUB DOCUMENTATION FILE
1505 public XmlElement StubType (TypeDefinition type, string output)
1507 string typesig = typeFormatters [0].GetDeclaration (type);
1508 if (typesig == null) return null; // not publicly visible
1510 XmlDocument doc = new XmlDocument();
1511 XmlElement root = doc.CreateElement("Type");
1512 doc.AppendChild (root);
1514 DoUpdateType2 ("New Type", doc, type, output, true);
1519 private XmlElement CreateSinceNode (XmlDocument doc)
1521 XmlElement s = doc.CreateElement ("since");
1522 s.SetAttribute ("version", since);
1526 // STUBBING/UPDATING FUNCTIONS
1528 public void UpdateType (XmlElement root, TypeDefinition type)
1530 root.SetAttribute("Name", GetDocTypeName (type));
1531 root.SetAttribute("FullName", GetDocTypeFullName (type));
1533 foreach (MemberFormatter f in typeFormatters) {
1534 string element = "TypeSignature[@Language='" + f.Language + "']";
1535 string valueToUse = f.GetDeclaration (type);
1538 root.SelectNodes (element).Cast<XmlElement> ().ToArray (),
1539 x => x.GetAttribute ("Value") == valueToUse,
1540 x => x.SetAttribute ("Value", valueToUse),
1542 var node = WriteElementAttribute (root, element, "Language", f.Language, forceNewElement: true);
1543 var newnode = WriteElementAttribute (root, node, "Value", valueToUse);
1549 AddAssemblyNameToNode (root, type);
1551 string assemblyInfoNodeFilter = MDocUpdater.HasDroppedNamespace (type) ? "[@apistyle='unified']" : "[not(@apistyle) or @apistyle='classic']";
1552 Func<XmlElement, bool> assemblyFilter = x => x.SelectSingleNode ("AssemblyName").InnerText == type.Module.Assembly.Name.Name;
1553 foreach(var ass in root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ().Where (assemblyFilter))
1555 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1556 if (!no_assembly_versions) {
1557 UpdateAssemblyVersions (ass, type, true);
1560 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1561 foreach (var version in versions)
1562 ass.RemoveChild (version);
1564 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1565 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1567 ClearElement(ass, "AssemblyCulture");
1570 // Why-oh-why do we put assembly attributes in each type file?
1571 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1572 // since they're outdated in current docs, and a waste of space.
1573 //MakeAttributes(ass, type.Assembly, true);
1574 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1575 if (assattrs != null)
1576 ass.RemoveChild(assattrs);
1578 NormalizeWhitespace(ass);
1581 if (type.IsGenericType ()) {
1582 MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
1584 ClearElement(root, "TypeParameters");
1587 if (type.BaseType != null) {
1588 XmlElement basenode = WriteElement(root, "Base");
1590 string basetypename = GetDocTypeFullName (type.BaseType);
1591 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1592 WriteElementText(root, "Base/BaseTypeName", basetypename);
1594 // Document how this type instantiates the generic parameters of its base type
1595 TypeReference origBase = type.BaseType.GetElementType ();
1596 if (origBase.IsGenericType ()) {
1597 ClearElement(basenode, "BaseTypeArguments");
1598 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1599 IList<TypeReference> baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1600 IList<GenericParameter> baseGenParams = origBase.GenericParameters;
1601 if (baseGenArgs.Count != baseGenParams.Count)
1602 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1603 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1604 GenericParameter param = baseGenParams [i];
1605 TypeReference value = baseGenArgs [i];
1607 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1608 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1609 bta.AppendChild(arg);
1610 arg.SetAttribute ("TypeParamName", param.Name);
1611 arg.InnerText = GetDocTypeFullName (value);
1615 ClearElement(root, "Base");
1618 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1619 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1620 List<string> interface_names = userInterfaces
1621 .Select (iface => GetDocTypeFullName (iface))
1625 XmlElement interfaces = WriteElement(root, "Interfaces");
1626 interfaces.RemoveAll();
1627 foreach (string iname in interface_names) {
1628 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1629 interfaces.AppendChild(iface);
1630 WriteElementText(iface, "InterfaceName", iname);
1633 ClearElement(root, "Interfaces");
1636 MakeAttributes (root, GetCustomAttributes (type), type);
1638 if (DocUtils.IsDelegate (type)) {
1639 MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
1640 var member = type.GetMethod ("Invoke");
1641 MakeParameters(root, member, member.Parameters);
1642 MakeReturnValue(root, member);
1645 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1646 MakeDocNode (typeInfo);
1648 if (!DocUtils.IsDelegate (type))
1649 WriteElement (root, "Members");
1651 OrderTypeNodes (root, root.ChildNodes);
1652 NormalizeWhitespace(root);
1655 /// <summary>Adds an AssemblyInfo with AssemblyName node to an XmlElement.</summary>
1656 /// <returns>The assembly that was either added, or was already present</returns>
1657 static XmlElement AddAssemblyNameToNode (XmlElement root, TypeDefinition type)
1659 return AddAssemblyNameToNode (root, type.Module);
1662 /// <summary>Adds an AssemblyInfo with AssemblyName node to an XmlElement.</summary>
1663 /// <returns>The assembly that was either added, or was already present</returns>
1664 static XmlElement AddAssemblyNameToNode (XmlElement root, ModuleDefinition module)
1666 Func<XmlElement, bool> assemblyFilter = x => x.SelectSingleNode ("AssemblyName").InnerText == module.Assembly.Name.Name;
1667 return AddAssemblyXmlNode (
1668 root.SelectNodes ("AssemblyInfo").Cast<XmlElement> ().ToArray (),
1669 assemblyFilter, x => WriteElementText (x, "AssemblyName", module.Assembly.Name.Name),
1671 XmlElement ass = WriteElement (root, "AssemblyInfo", forceNewElement: true);
1672 if (MDocUpdater.HasDroppedNamespace (module))
1673 ass.SetAttribute ("apistyle", "unified");
1678 static readonly string[] TypeNodeOrder = {
1682 "ThreadingSafetyStatement",
1683 "ThreadSafetyStatement",
1695 static void OrderTypeNodes (XmlNode member, XmlNodeList children)
1697 ReorderNodes (member, children, TypeNodeOrder);
1700 internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1702 List<T> l = new List<T> (list);
1707 private void UpdateMember (DocsNodeInfo info)
1709 XmlElement me = (XmlElement) info.Node;
1710 MemberReference mi = info.Member;
1712 foreach (MemberFormatter f in memberFormatters) {
1713 string element = "MemberSignature[@Language='" + f.Language + "']";
1715 var valueToUse = f.GetDeclaration (mi);
1718 me.SelectNodes (element).Cast<XmlElement> ().ToArray(),
1719 x => x.GetAttribute("Value") == valueToUse,
1720 x => x.SetAttribute ("Value", valueToUse),
1722 var node = WriteElementAttribute (me, element, "Language", f.Language, forceNewElement:true);
1723 var newNode = WriteElementAttribute (me, node, "Value", valueToUse);
1730 WriteElementText(me, "MemberType", GetMemberType(mi));
1732 if (!no_assembly_versions) {
1734 UpdateAssemblyVersions (me, mi, true);
1736 var node = AddAssemblyNameToNode (me, mi.Module);
1738 UpdateAssemblyVersionForAssemblyInfo (node, me, new[] { GetAssemblyVersion (mi.Module.Assembly) }, add: true);
1742 ClearElement (me, "AssemblyInfo");
1745 MakeAttributes (me, GetCustomAttributes (mi), mi.DeclaringType);
1747 MakeReturnValue(me, mi, MDocUpdater.HasDroppedNamespace(mi));
1748 if (mi is MethodReference) {
1749 MethodReference mb = (MethodReference) mi;
1750 if (mb.IsGenericMethod ())
1751 MakeTypeParameters (me, mb.GenericParameters, mi, MDocUpdater.HasDroppedNamespace(mi));
1753 MakeParameters(me, mi, MDocUpdater.HasDroppedNamespace(mi));
1756 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1757 WriteElementText(me, "MemberValue", fieldValue);
1759 info.Node = WriteElement (me, "Docs");
1761 OrderMemberNodes (me, me.ChildNodes);
1762 UpdateExtensionMethods (me, info);
1765 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, MemberReference member) {
1766 AddXmlNode (relevant, valueMatches, setValue, makeNewNode, member.Module);
1769 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, TypeDefinition type) {
1770 AddXmlNode (relevant, valueMatches, setValue, makeNewNode, type.Module);
1773 static XmlElement AddAssemblyXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, ModuleDefinition module)
1775 bool isUnified = MDocUpdater.HasDroppedNamespace (module);
1776 XmlElement thisAssemblyNode = relevant.FirstOrDefault (valueMatches);
1777 if (thisAssemblyNode == null) {
1778 thisAssemblyNode = makeNewNode ();
1779 setValue (thisAssemblyNode);
1783 thisAssemblyNode.AddApiStyle (ApiStyle.Unified);
1785 foreach (var otherNodes in relevant.Where (n => n != thisAssemblyNode && n.DoesNotHaveApiStyle (ApiStyle.Unified))) {
1786 otherNodes.AddApiStyle (ApiStyle.Classic);
1789 return thisAssemblyNode;
1792 /// <summary>Adds an xml node, reusing the node if it's available</summary>
1793 /// <param name="relevant">The existing set of nodes</param>
1794 /// <param name="valueMatches">Checks to see if the node's value matches what you're trying to write.</param>
1795 /// <param name="setValue">Sets the node's value</param>
1796 /// <param name="makeNewNode">Creates a new node, if valueMatches returns false.</param>
1797 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, ModuleDefinition module)
1799 bool shouldDuplicate = MDocUpdater.HasDroppedNamespace (module);
1800 var styleToUse = shouldDuplicate ? ApiStyle.Unified : ApiStyle.Classic;
1801 var existing = relevant;
1803 bool addedOldApiStyle = false;
1805 if (shouldDuplicate) {
1806 existing = existing.Where (n => n.HasApiStyle (styleToUse)).ToArray ();
1807 foreach (var n in relevant.Where (n => n.DoesNotHaveApiStyle (styleToUse))) {
1808 if (valueMatches (n)) {
1812 n.AddApiStyle (ApiStyle.Classic);
1813 addedOldApiStyle = true;
1818 if (!existing.Any ()) {
1819 var newNode = makeNewNode ();
1820 if (shouldDuplicate && addedOldApiStyle) {
1821 newNode.AddApiStyle (ApiStyle.Unified);
1825 var itemToReuse = existing.First ();
1826 setValue (itemToReuse);
1828 if (shouldDuplicate && addedOldApiStyle) {
1829 itemToReuse.AddApiStyle (styleToUse);
1835 static readonly string[] MemberNodeOrder = {
1850 static void OrderMemberNodes (XmlNode member, XmlNodeList children)
1852 ReorderNodes (member, children, MemberNodeOrder);
1855 static void ReorderNodes (XmlNode node, XmlNodeList children, string[] ordering)
1857 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1858 for (int i = 0; i < ordering.Length; ++i) {
1859 for (int j = 0; j < children.Count; ++j) {
1860 XmlNode c = children [j];
1861 if (c.Name == ordering [i]) {
1862 newChildren.Add (c);
1866 if (newChildren.Count >= 0)
1867 node.PrependChild ((XmlNode) newChildren [0]);
1868 for (int i = 1; i < newChildren.Count; ++i) {
1869 XmlNode prev = (XmlNode) newChildren [i-1];
1870 XmlNode cur = (XmlNode) newChildren [i];
1871 node.RemoveChild (cur);
1872 node.InsertAfter (cur, prev);
1876 IEnumerable<string> GetCustomAttributes (MemberReference mi)
1878 IEnumerable<string> attrs = Enumerable.Empty<string>();
1880 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1882 attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1884 PropertyDefinition pd = mi as PropertyDefinition;
1886 if (pd.GetMethod != null)
1887 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1888 if (pd.SetMethod != null)
1889 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1892 EventDefinition ed = mi as EventDefinition;
1894 if (ed.AddMethod != null)
1895 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1896 if (ed.RemoveMethod != null)
1897 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1903 IEnumerable<string> GetCustomAttributes (IList<CustomAttribute> attributes, string prefix)
1905 foreach (CustomAttribute attribute in attributes.OrderBy (ca => ca.AttributeType.FullName)) {
1907 TypeDefinition attrType = attribute.AttributeType as TypeDefinition;
1908 if (attrType != null && !IsPublic (attrType))
1910 if (slashdocFormatter.GetName (attribute.AttributeType) == null)
1913 if (Array.IndexOf (IgnorableAttributes, attribute.AttributeType.FullName) >= 0)
1916 StringList fields = new StringList ();
1918 for (int i = 0; i < attribute.ConstructorArguments.Count; ++i) {
1919 CustomAttributeArgument argument = attribute.ConstructorArguments [i];
1920 fields.Add (MakeAttributesValueString (
1925 (from namedArg in attribute.Fields
1926 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value })
1928 (from namedArg in attribute.Properties
1929 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value }))
1930 .OrderBy (v => v.Name);
1931 foreach (var d in namedArgs)
1932 fields.Add (string.Format ("{0}={1}", d.Name,
1933 MakeAttributesValueString (d.Value, d.Type)));
1935 string a2 = String.Join(", ", fields.ToArray ());
1936 if (a2 != "") a2 = "(" + a2 + ")";
1938 string name = attribute.GetDeclaringType();
1939 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1940 yield return prefix + name + a2;
1944 static readonly string[] ValidExtensionMembers = {
1953 static readonly string[] ValidExtensionDocMembers = {
1959 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1961 MethodDefinition me = info.Member as MethodDefinition;
1964 if (info.Parameters.Count < 1)
1966 if (!DocUtils.IsExtensionMethod (me))
1969 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1970 XmlNode member = e.CloneNode (true);
1971 em.AppendChild (member);
1972 RemoveExcept (member, ValidExtensionMembers);
1973 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1974 WriteElementText (member, "MemberType", "ExtensionMethod");
1975 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1976 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1977 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1978 member.AppendChild (link);
1979 AddTargets (em, info);
1981 extensionMethods.Add (em);
1984 private static void RemoveExcept (XmlNode node, string[] except)
1988 MyXmlNodeList remove = null;
1989 foreach (XmlNode n in node.ChildNodes) {
1990 if (Array.BinarySearch (except, n.Name) < 0) {
1992 remove = new MyXmlNodeList ();
1997 foreach (XmlNode n in remove)
1998 node.RemoveChild (n);
2001 private static void AddTargets (XmlNode member, DocsNodeInfo info)
2003 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
2004 member.PrependChild (targets);
2005 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
2006 AppendElementAttributeText (targets, "Target", "Type",
2007 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
2010 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
2011 IList<TypeReference> constraints = gp.Constraints;
2012 if (constraints.Count == 0)
2013 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
2015 foreach (TypeReference c in constraints)
2016 AppendElementAttributeText(targets, "Target", "Type",
2017 slashdocFormatter.GetDeclaration (c));
2021 private static bool GetFieldConstValue (FieldDefinition field, out string value)
2024 TypeDefinition type = field.DeclaringType.Resolve ();
2025 if (type != null && type.IsEnum) return false;
2027 if (type != null && type.IsGenericType ()) return false;
2028 if (!field.HasConstant)
2030 if (field.IsLiteral) {
2031 object val = field.Constant;
2032 if (val == null) value = "null";
2033 else if (val is Enum) value = val.ToString();
2034 else if (val is IFormattable) {
2035 value = ((IFormattable)val).ToString();
2037 value = "\"" + value + "\"";
2039 if (value != null && value != "")
2045 // XML HELPER FUNCTIONS
2047 internal static XmlElement WriteElement(XmlNode parent, string element, bool forceNewElement = false) {
2048 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
2049 if (ret == null || forceNewElement) {
2050 string[] path = element.Split('/');
2051 foreach (string p in path) {
2052 ret = (XmlElement)parent.SelectSingleNode(p);
2053 if (ret == null || forceNewElement) {
2055 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
2056 ename = ename.Substring(0, ename.IndexOf('['));
2057 ret = parent.OwnerDocument.CreateElement(ename);
2058 parent.AppendChild(ret);
2067 private static XmlElement WriteElementText(XmlNode parent, string element, string value, bool forceNewElement = false) {
2068 XmlElement node = WriteElement(parent, element, forceNewElement: forceNewElement);
2069 node.InnerText = value;
2073 static XmlElement AppendElementText (XmlNode parent, string element, string value)
2075 XmlElement n = parent.OwnerDocument.CreateElement (element);
2076 parent.AppendChild (n);
2077 n.InnerText = value;
2081 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
2083 XmlElement n = parent.OwnerDocument.CreateElement (element);
2084 parent.AppendChild (n);
2085 n.SetAttribute (attribute, value);
2089 internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
2091 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
2092 dest.AppendChild (copy);
2096 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
2097 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
2100 node = WriteElement(parent, element);
2101 node.InnerText = value;
2103 private static XmlElement WriteElementAttribute(XmlElement parent, string element, string attribute, string value, bool forceNewElement = false) {
2104 XmlElement node = WriteElement(parent, element, forceNewElement:forceNewElement);
2105 return WriteElementAttribute (parent, node, attribute, value);
2107 private static XmlElement WriteElementAttribute(XmlElement parent, XmlElement node, string attribute, string value) {
2108 if (node.GetAttribute (attribute) != value) {
2109 node.SetAttribute (attribute, value);
2113 internal static void ClearElement(XmlElement parent, string name) {
2114 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
2116 parent.RemoveChild(node);
2119 // DOCUMENTATION HELPER FUNCTIONS
2121 private void MakeDocNode (DocsNodeInfo info)
2123 List<GenericParameter> genericParams = info.GenericParameters;
2124 IList<ParameterDefinition> parameters = info.Parameters;
2125 TypeReference returntype = info.ReturnType;
2126 bool returnisreturn = info.ReturnIsReturn;
2127 XmlElement e = info.Node;
2128 bool addremarks = info.AddRemarks;
2130 WriteElementInitialText(e, "summary", "To be added.");
2132 if (parameters != null) {
2133 string[] values = new string [parameters.Count];
2134 for (int i = 0; i < values.Length; ++i)
2135 values [i] = parameters [i].Name;
2136 UpdateParameters (e, "param", values);
2139 if (genericParams != null) {
2140 string[] values = new string [genericParams.Count];
2141 for (int i = 0; i < values.Length; ++i)
2142 values [i] = genericParams [i].Name;
2143 UpdateParameters (e, "typeparam", values);
2146 string retnodename = null;
2147 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
2148 retnodename = returnisreturn ? "returns" : "value";
2149 string retnodename_other = !returnisreturn ? "returns" : "value";
2151 // If it has a returns node instead of a value node, change its name.
2152 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
2153 if (retother != null) {
2154 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
2155 foreach (XmlNode node in retother)
2156 retnode.AppendChild(node.CloneNode(true));
2157 e.ReplaceChild(retnode, retother);
2159 WriteElementInitialText(e, retnodename, "To be added.");
2162 ClearElement(e, "returns");
2163 ClearElement(e, "value");
2167 WriteElementInitialText(e, "remarks", "To be added.");
2169 if (exceptions.HasValue && info.Member != null &&
2170 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
2171 UpdateExceptions (e, info.Member);
2174 foreach (DocumentationImporter importer in importers)
2175 importer.ImportDocumentation (info);
2177 OrderDocsNodes (e, e.ChildNodes);
2178 NormalizeWhitespace(e);
2181 static readonly string[] DocsNodeOrder = {
2182 "typeparam", "param", "summary", "returns", "value", "remarks",
2185 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
2187 ReorderNodes (docs, children, DocsNodeOrder);
2191 private void UpdateParameters (XmlElement e, string element, string[] values)
2193 if (values != null) {
2194 XmlNode[] paramnodes = new XmlNode[values.Length];
2196 // Some documentation had param nodes with leading spaces.
2197 foreach (XmlElement paramnode in e.SelectNodes(element)){
2198 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
2201 // If a member has only one parameter, we can track changes to
2202 // the name of the parameter easily.
2203 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
2204 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
2207 bool reinsert = false;
2209 // Pick out existing and still-valid param nodes, and
2210 // create nodes for parameters not in the file.
2211 Hashtable seenParams = new Hashtable();
2212 for (int pi = 0; pi < values.Length; pi++) {
2213 string p = values [pi];
2216 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
2217 if (paramnodes[pi] != null) continue;
2219 XmlElement pe = e.OwnerDocument.CreateElement(element);
2220 pe.SetAttribute("name", p);
2221 pe.InnerText = "To be added.";
2222 paramnodes[pi] = pe;
2226 // Remove parameters that no longer exist and check all params are in the right order.
2228 MyXmlNodeList todelete = new MyXmlNodeList ();
2229 foreach (XmlElement paramnode in e.SelectNodes(element)) {
2230 string name = paramnode.GetAttribute("name");
2231 if (!seenParams.ContainsKey(name)) {
2232 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2233 Warning ("The following param node can only be deleted if the --delete option is given: ");
2234 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
2236 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
2237 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2241 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
2242 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2243 e.ParentNode.Attributes ["MemberName"].Value,
2246 Warning ("\tValue={0}", paramnode.OuterXml);
2248 todelete.Add (paramnode);
2253 if ((int)seenParams[name] != idx)
2259 foreach (XmlNode n in todelete) {
2260 n.ParentNode.RemoveChild (n);
2263 // Re-insert the parameter nodes at the top of the doc section.
2265 for (int pi = values.Length-1; pi >= 0; pi--)
2266 e.PrependChild(paramnodes[pi]);
2268 // Clear all existing param nodes
2269 foreach (XmlNode paramnode in e.SelectNodes(element)) {
2270 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2271 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
2272 Console.WriteLine(paramnode.OuterXml);
2274 paramnode.ParentNode.RemoveChild(paramnode);
2280 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
2282 string existingName = pe.GetAttribute ("name");
2283 pe.SetAttribute ("name", newName);
2284 if (existingName == newName)
2286 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
2287 if (paramref.GetAttribute ("name").Trim () == existingName)
2288 paramref.SetAttribute ("name", newName);
2291 class CrefComparer : XmlNodeComparer {
2293 public CrefComparer ()
2297 public override int Compare (XmlNode x, XmlNode y)
2299 string xType = x.Attributes ["cref"].Value;
2300 string yType = y.Attributes ["cref"].Value;
2301 string xNamespace = GetNamespace (xType);
2302 string yNamespace = GetNamespace (yType);
2304 int c = xNamespace.CompareTo (yNamespace);
2307 return xType.CompareTo (yType);
2310 static string GetNamespace (string type)
2312 int n = type.LastIndexOf ('.');
2314 return type.Substring (0, n);
2315 return string.Empty;
2319 private void UpdateExceptions (XmlNode docs, MemberReference member)
2321 string indent = new string (' ', 10);
2322 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
2323 string cref = slashdocFormatter.GetDeclaration (source.Exception);
2324 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
2327 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
2328 e.SetAttribute ("cref", cref);
2329 e.InnerXml = "To be added; from:\n" + indent + "<see cref=\"" +
2330 string.Join ("\" />,\n" + indent + "<see cref=\"",
2331 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
2332 .OrderBy (s => s)) +
2334 docs.AppendChild (e);
2336 SortXmlNodes (docs, docs.SelectNodes ("exception"),
2337 new CrefComparer ());
2340 private static void NormalizeWhitespace(XmlElement e) {
2341 // Remove all text and whitespace nodes from the element so it
2342 // is outputted with nice indentation and no blank lines.
2343 ArrayList deleteNodes = new ArrayList();
2344 foreach (XmlNode n in e)
2345 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2347 foreach (XmlNode n in deleteNodes)
2348 n.ParentNode.RemoveChild(n);
2351 private static bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
2353 TypeDefinition type = member as TypeDefinition;
2355 type = member.DeclaringType as TypeDefinition;
2357 var versions = new string[] { GetAssemblyVersion (type.Module.Assembly) };
2359 if (root.LocalName == "AssemblyInfo")
2360 return UpdateAssemblyVersionForAssemblyInfo (root, root.ParentNode as XmlElement, versions, add: true);
2362 return UpdateAssemblyVersions (root, type.Module.Assembly, versions, add);
2365 private static string GetAssemblyVersion (AssemblyDefinition assembly)
2367 return assembly.Name.Version.ToString();
2370 private static bool UpdateAssemblyVersions(XmlElement root, AssemblyDefinition assembly, string[] assemblyVersions, bool add)
2372 XmlElement av = (XmlElement) root.SelectSingleNode ("AssemblyVersions");
2374 // AssemblyVersions is not part of the spec
2375 root.RemoveChild (av);
2378 string oldNodeFilter = "AssemblyInfo[not(@apistyle) or @apistyle='classic']";
2379 string newNodeFilter = "AssemblyInfo[@apistyle='unified']";
2380 string thisNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? newNodeFilter : oldNodeFilter;
2381 string thatNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? oldNodeFilter : newNodeFilter;
2383 XmlElement e = (XmlElement) root.SelectSingleNode (thisNodeFilter);
2385 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2387 if (MDocUpdater.HasDroppedNamespace (assembly)) {
2388 e.SetAttribute ("apistyle", "unified");
2391 root.AppendChild(e);
2394 var thatNode = (XmlElement) root.SelectSingleNode (thatNodeFilter);
2395 if (MDocUpdater.HasDroppedNamespace (assembly) && thatNode != null) {
2396 // there's a classic node, we should add apistyles
2397 e.SetAttribute ("apistyle", "unified");
2398 thatNode.SetAttribute ("apistyle", "classic");
2401 return UpdateAssemblyVersionForAssemblyInfo (e, root, assemblyVersions, add);
2404 static bool UpdateAssemblyVersionForAssemblyInfo (XmlElement e, XmlElement root, string[] assemblyVersions, bool add)
2406 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().Where (v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0).ToList ();
2407 // matches.Count > 0 && add: ignore -- already present
2408 if (matches.Count > 0 && !add) {
2409 foreach (XmlNode c in matches)
2412 else if (matches.Count == 0 && add) {
2413 foreach (string sv in assemblyVersions) {
2414 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2420 // matches.Count == 0 && !add: ignore -- already not present
2421 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2422 SortXmlNodes (e, avs, new VersionComparer ());
2424 bool anyNodesLeft = avs.Count != 0;
2425 if (!anyNodesLeft) {
2426 e.ParentNode.RemoveChild (e);
2428 return anyNodesLeft;
2431 // FIXME: get TypeReferences instead of string comparison?
2432 private static string[] IgnorableAttributes = {
2433 // Security related attributes
2434 "System.Reflection.AssemblyKeyFileAttribute",
2435 "System.Reflection.AssemblyDelaySignAttribute",
2436 // Present in @RefType
2437 "System.Runtime.InteropServices.OutAttribute",
2438 // For naming the indexer to use when not using indexers
2439 "System.Reflection.DefaultMemberAttribute",
2440 // for decimal constants
2441 "System.Runtime.CompilerServices.DecimalConstantAttribute",
2442 // compiler generated code
2443 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
2444 // more compiler generated code, e.g. iterator methods
2445 "System.Diagnostics.DebuggerHiddenAttribute",
2446 "System.Runtime.CompilerServices.FixedBufferAttribute",
2447 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
2448 // extension methods
2449 "System.Runtime.CompilerServices.ExtensionAttribute",
2450 // Used to differentiate 'object' from C#4 'dynamic'
2451 "System.Runtime.CompilerServices.DynamicAttribute",
2454 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes, TypeReference t=null)
2456 if (!attributes.Any ()) {
2457 ClearElement (root, "Attributes");
2461 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2465 e = root.OwnerDocument.CreateElement("Attributes");
2467 foreach (string attribute in attributes) {
2468 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2471 WriteElementText(ae, "AttributeName", attribute);
2474 if (e.ParentNode == null)
2475 root.AppendChild(e);
2477 NormalizeWhitespace(e);
2480 public static string MakeAttributesValueString (object v, TypeReference valueType)
2482 var formatters = new [] {
2483 new AttributeValueFormatter (),
2484 new ApplePlatformEnumFormatter (),
2485 new StandardFlagsEnumFormatter (),
2486 new DefaultAttributeValueFormatter (),
2489 ResolvedTypeInfo type = new ResolvedTypeInfo (valueType);
2490 foreach (var formatter in formatters) {
2491 string formattedValue;
2492 if (formatter.TryFormatValue (v, type, out formattedValue)) {
2493 return formattedValue;
2497 // this should never occur because the DefaultAttributeValueFormatter will always
2498 // successfully format the value ... but this is needed to satisfy the compiler :)
2499 throw new InvalidDataException (string.Format ("Unable to format attribute value ({0})", v.ToString ()));
2502 internal static IDictionary<long, string> GetEnumerationValues (TypeDefinition type)
2504 var values = new Dictionary<long, string> ();
2506 (from f in type.Fields
2507 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
2509 values [ToInt64 (f.Constant)] = f.Name;
2514 internal static long ToInt64 (object value)
2517 return (long) (ulong) value;
2518 return Convert.ToInt64 (value);
2521 private void MakeParameters (XmlElement root, MemberReference member, IList<ParameterDefinition> parameters, bool shouldDuplicateWithNew=false)
2523 XmlElement e = WriteElement(root, "Parameters");
2526 foreach (ParameterDefinition p in parameters) {
2530 var ptype = GetDocParameterType (p.ParameterType);
2531 var newPType = ptype;
2533 if (MDocUpdater.SwitchingToMagicTypes) {
2534 newPType = NativeTypeManager.ConvertFromNativeType (ptype);
2537 // now find the existing node, if it's there so we can reuse it.
2538 var nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2539 .Cast<XmlElement> ().Where (x => x.GetAttribute ("Name") == p.Name)
2542 if (nodes.Count () == 0) {
2543 // wasn't found, let's make sure it wasn't just cause the param name was changed
2544 nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2545 .Cast<XmlElement> ()
2546 .Skip (i) // this makes sure we don't inadvertently "reuse" nodes when adding new ones
2547 .Where (x => x.GetAttribute ("Name") != p.Name && (x.GetAttribute ("Type") == ptype || x.GetAttribute ("Type") == newPType))
2548 .Take(1) // there might be more than one that meets this parameter ... only take the first.
2553 x => x.GetAttribute ("Type") == ptype,
2554 x => x.SetAttribute ("Type", ptype),
2556 pe = root.OwnerDocument.CreateElement ("Parameter");
2559 pe.SetAttribute ("Name", p.Name);
2560 pe.SetAttribute ("Type", ptype);
2561 if (p.ParameterType is ByReferenceType) {
2563 pe.SetAttribute ("RefType", "out");
2565 pe.SetAttribute ("RefType", "ref");
2568 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
2577 private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams, MemberReference member, bool shouldDuplicateWithNew)
2579 if (typeParams == null || typeParams.Count == 0) {
2580 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2582 root.RemoveChild (f);
2585 XmlElement e = WriteElement(root, "TypeParameters");
2587 var nodes = e.SelectNodes ("TypeParameter").Cast<XmlElement> ().ToArray ();
2589 foreach (GenericParameter t in typeParams) {
2591 IList<TypeReference> constraints = t.Constraints;
2592 GenericParameterAttributes attrs = t.Attributes;
2598 var baseType = e.SelectSingleNode("BaseTypeName");
2599 // TODO: should this comparison take into account BaseTypeName?
2600 return x.GetAttribute("Name") == t.Name;
2602 x => {}, // no additional action required
2605 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2607 pe.SetAttribute("Name", t.Name);
2608 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""), t.DeclaringType);
2609 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2610 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2618 ce = root.OwnerDocument.CreateElement ("Constraints");
2620 pe.AppendChild (ce);
2621 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2622 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2623 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2624 AppendElementText (ce, "ParameterAttribute", "Covariant");
2625 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2626 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2627 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2628 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2629 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2630 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2631 foreach (TypeReference c in constraints) {
2632 TypeDefinition cd = c.Resolve ();
2633 AppendElementText (ce,
2634 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2635 GetDocTypeFullName (c));
2644 private void MakeParameters (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew)
2646 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2647 MakeParameters (root, mi, ((MethodDefinition)mi).Parameters, shouldDuplicateWithNew);
2648 else if (mi is MethodDefinition) {
2649 MethodDefinition mb = (MethodDefinition) mi;
2650 IList<ParameterDefinition> parameters = mb.Parameters;
2651 MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
2652 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2653 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2654 p.SetAttribute ("RefType", "this");
2657 else if (mi is PropertyDefinition) {
2658 IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
2659 if (parameters.Count > 0)
2660 MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
2664 else if (mi is FieldDefinition) return;
2665 else if (mi is EventDefinition) return;
2666 else throw new ArgumentException();
2669 internal static string GetDocParameterType (TypeReference type)
2671 return GetDocTypeFullName (type).Replace ("@", "&");
2674 private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes, bool shouldDuplicateWithNew=false)
2676 XmlElement e = WriteElement(root, "ReturnValue");
2677 var valueToUse = GetDocTypeFullName (type);
2679 AddXmlNode (e.SelectNodes("ReturnType").Cast<XmlElement> ().ToArray (),
2680 x => x.InnerText == valueToUse,
2681 x => x.InnerText = valueToUse,
2683 var newNode = WriteElementText(e, "ReturnType", valueToUse, forceNewElement: true);
2684 if (attributes != null)
2685 MakeAttributes(e, GetCustomAttributes (attributes, ""), type);
2692 private void MakeReturnValue (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew=false)
2694 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2696 else if (mi is MethodDefinition)
2697 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes, shouldDuplicateWithNew);
2698 else if (mi is PropertyDefinition)
2699 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null, shouldDuplicateWithNew);
2700 else if (mi is FieldDefinition)
2701 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null, shouldDuplicateWithNew);
2702 else if (mi is EventDefinition)
2703 MakeReturnValue (root, ((EventDefinition)mi).EventType, null, shouldDuplicateWithNew);
2705 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2708 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2710 MemberReference mi = info.Member;
2711 if (mi is TypeDefinition) return null;
2713 string sigs = memberFormatters [0].GetDeclaration (mi);
2714 if (sigs == null) return null; // not publicly visible
2716 // no documentation for property/event accessors. Is there a better way of doing this?
2717 if (mi.Name.StartsWith("get_")) return null;
2718 if (mi.Name.StartsWith("set_")) return null;
2719 if (mi.Name.StartsWith("add_")) return null;
2720 if (mi.Name.StartsWith("remove_")) return null;
2721 if (mi.Name.StartsWith("raise_")) return null;
2723 XmlElement me = doc.CreateElement("Member");
2724 me.SetAttribute("MemberName", GetMemberName (mi));
2728 if (exceptions.HasValue &&
2729 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2730 UpdateExceptions (info.Node, info.Member);
2732 if (since != null) {
2733 XmlNode docs = me.SelectSingleNode("Docs");
2734 docs.AppendChild (CreateSinceNode (doc));
2740 internal static string GetMemberName (MemberReference mi)
2742 MethodDefinition mb = mi as MethodDefinition;
2744 PropertyDefinition pi = mi as PropertyDefinition;
2747 return DocUtils.GetPropertyName (pi);
2749 StringBuilder sb = new StringBuilder (mi.Name.Length);
2750 if (!DocUtils.IsExplicitlyImplemented (mb))
2751 sb.Append (mi.Name);
2753 TypeReference iface;
2754 MethodReference ifaceMethod;
2755 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2756 sb.Append (GetDocTypeFullName (iface));
2758 sb.Append (ifaceMethod.Name);
2760 if (mb.IsGenericMethod ()) {
2761 IList<GenericParameter> typeParams = mb.GenericParameters;
2762 if (typeParams.Count > 0) {
2764 sb.Append (typeParams [0].Name);
2765 for (int i = 1; i < typeParams.Count; ++i)
2766 sb.Append (",").Append (typeParams [i].Name);
2770 return sb.ToString ();
2773 /// SIGNATURE GENERATION FUNCTIONS
2774 internal static bool IsPrivate (MemberReference mi)
2776 return memberFormatters [0].GetDeclaration (mi) == null;
2779 internal static string GetMemberType (MemberReference mi)
2781 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2782 return "Constructor";
2783 if (mi is MethodDefinition)
2785 if (mi is PropertyDefinition)
2787 if (mi is FieldDefinition)
2789 if (mi is EventDefinition)
2791 throw new ArgumentException();
2794 private static string GetDocTypeName (TypeReference type)
2796 return docTypeFormatter.GetName (type);
2799 internal static string GetDocTypeFullName (TypeReference type)
2801 return DocTypeFullMemberFormatter.Default.GetName (type);
2804 internal static string GetXPathForMember (DocumentationMember member)
2806 StringBuilder xpath = new StringBuilder ();
2807 xpath.Append ("//Members/Member[@MemberName=\"")
2808 .Append (member.MemberName)
2810 if (member.Parameters != null && member.Parameters.Count > 0) {
2811 xpath.Append ("/Parameters[count(Parameter) = ")
2812 .Append (member.Parameters.Count);
2813 for (int i = 0; i < member.Parameters.Count; ++i) {
2814 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2815 xpath.Append (member.Parameters [i]);
2816 xpath.Append ("\"");
2818 xpath.Append ("]/..");
2820 return xpath.ToString ();
2823 public static string GetXPathForMember (XPathNavigator member)
2825 StringBuilder xpath = new StringBuilder ();
2826 xpath.Append ("//Type[@FullName=\"")
2827 .Append (member.SelectSingleNode ("../../@FullName").Value)
2829 xpath.Append ("Members/Member[@MemberName=\"")
2830 .Append (member.SelectSingleNode ("@MemberName").Value)
2832 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2833 if (parameters.Count > 0) {
2834 xpath.Append ("/Parameters[count(Parameter) = ")
2835 .Append (parameters.Count);
2837 while (parameters.MoveNext ()) {
2839 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2840 xpath.Append (parameters.Current.Value);
2841 xpath.Append ("\"");
2843 xpath.Append ("]/..");
2845 return xpath.ToString ();
2848 public static string GetXPathForMember (MemberReference member)
2850 StringBuilder xpath = new StringBuilder ();
2851 xpath.Append ("//Type[@FullName=\"")
2852 .Append (member.DeclaringType.FullName)
2854 xpath.Append ("Members/Member[@MemberName=\"")
2855 .Append (GetMemberName (member))
2858 IList<ParameterDefinition> parameters = null;
2859 if (member is MethodDefinition)
2860 parameters = ((MethodDefinition) member).Parameters;
2861 else if (member is PropertyDefinition) {
2862 parameters = ((PropertyDefinition) member).Parameters;
2864 if (parameters != null && parameters.Count > 0) {
2865 xpath.Append ("/Parameters[count(Parameter) = ")
2866 .Append (parameters.Count);
2867 for (int i = 0; i < parameters.Count; ++i) {
2868 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2869 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2870 xpath.Append ("\"");
2872 xpath.Append ("]/..");
2874 return xpath.ToString ();
2878 static class CecilExtensions {
2879 public static string GetDeclaringType(this CustomAttribute attribute)
2881 var type = attribute.Constructor.DeclaringType;
2882 var typeName = type.FullName;
2884 string translatedType = NativeTypeManager.GetTranslatedName (type);
2885 return translatedType;
2888 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
2890 foreach (var c in type.Methods.Where (m => m.IsConstructor))
2891 yield return (MemberReference) c;
2892 foreach (var e in type.Events)
2893 yield return (MemberReference) e;
2894 foreach (var f in type.Fields)
2895 yield return (MemberReference) f;
2896 foreach (var m in type.Methods.Where (m => !m.IsConstructor))
2897 yield return (MemberReference) m;
2898 foreach (var t in type.NestedTypes)
2899 yield return (MemberReference) t;
2900 foreach (var p in type.Properties)
2901 yield return (MemberReference) p;
2904 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
2906 return GetMembers (type).Where (m => m.Name == member);
2909 public static MemberReference GetMember (this TypeDefinition type, string member)
2911 return GetMembers (type, member).EnsureZeroOrOne ();
2914 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2916 if (source.Count () > 1)
2917 throw new InvalidOperationException ("too many matches");
2918 return source.FirstOrDefault ();
2921 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2924 .Where (m => m.Name == method)
2925 .EnsureZeroOrOne ();
2928 public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
2930 TypeDefinition def = type as TypeDefinition;
2932 return new MemberReference [0];
2933 CustomAttribute defMemberAttr = def.CustomAttributes
2934 .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
2935 if (defMemberAttr == null)
2936 return new MemberReference [0];
2937 string name = (string) defMemberAttr.ConstructorArguments [0].Value;
2938 return def.Properties
2939 .Where (p => p.Name == name)
2940 .Select (p => (MemberReference) p);
2943 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2945 return assembly.Modules.SelectMany (md => md.GetAllTypes ());
2948 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2950 return GetTypes (assembly)
2951 .Where (td => td.FullName == type)
2952 .EnsureZeroOrOne ();
2955 public static bool IsGenericType (this TypeReference type)
2957 return type.GenericParameters.Count > 0;
2960 public static bool IsGenericMethod (this MethodReference method)
2962 return method.GenericParameters.Count > 0;
2965 public static MemberReference Resolve (this MemberReference member)
2967 FieldReference fr = member as FieldReference;
2969 return fr.Resolve ();
2970 MethodReference mr = member as MethodReference;
2972 return mr.Resolve ();
2973 TypeReference tr = member as TypeReference;
2975 return tr.Resolve ();
2976 PropertyReference pr = member as PropertyReference;
2979 EventReference er = member as EventReference;
2982 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2985 public static TypeReference GetUnderlyingType (this TypeDefinition type)
2989 return type.Fields.First (f => f.Name == "value__").FieldType;
2992 public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
2994 return self.Types.SelectMany (t => t.GetAllTypes ());
2997 static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
3001 if (!self.HasNestedTypes)
3004 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
3014 static class DocUtils {
3016 public static bool DoesNotHaveApiStyle(this XmlElement element, ApiStyle style) {
3017 string styleString = style.ToString ().ToLowerInvariant ();
3018 string apistylevalue = element.GetAttribute ("apistyle");
3019 return apistylevalue != styleString || string.IsNullOrWhiteSpace(apistylevalue);
3021 public static bool HasApiStyle(this XmlElement element, ApiStyle style) {
3022 string styleString = style.ToString ().ToLowerInvariant ();
3023 return element.GetAttribute ("apistyle") == styleString;
3025 public static bool HasApiStyle(this XmlNode node, ApiStyle style)
3027 var attribute = node.Attributes ["apistyle"];
3028 return attribute != null && attribute.Value == style.ToString ().ToLowerInvariant ();
3030 public static void AddApiStyle(this XmlElement element, ApiStyle style) {
3031 string styleString = style.ToString ().ToLowerInvariant ();
3032 var existingValue = element.GetAttribute ("apistyle");
3033 if (string.IsNullOrWhiteSpace (existingValue) || existingValue != styleString) {
3034 element.SetAttribute ("apistyle", styleString);
3037 public static void RemoveApiStyle (this XmlElement element, ApiStyle style)
3039 string styleString = style.ToString ().ToLowerInvariant ();
3040 string existingValue = element.GetAttribute ("apistyle");
3041 if (string.IsNullOrWhiteSpace (existingValue) || existingValue == styleString) {
3042 element.RemoveAttribute ("apistyle");
3045 public static void RemoveApiStyle (this XmlNode node, ApiStyle style)
3047 var styleAttribute = node.Attributes ["apistyle"];
3048 if (styleAttribute != null && styleAttribute.Value == style.ToString ().ToLowerInvariant ()) {
3049 node.Attributes.Remove (styleAttribute);
3053 public static bool IsExplicitlyImplemented (MethodDefinition method)
3055 return method.IsPrivate && method.IsFinal && method.IsVirtual;
3058 public static string GetTypeDotMember (string name)
3060 int startType, startMethod;
3061 startType = startMethod = -1;
3062 for (int i = 0; i < name.Length; ++i) {
3063 if (name [i] == '.') {
3064 startType = startMethod;
3068 return name.Substring (startType+1);
3071 public static string GetMember (string name)
3073 int i = name.LastIndexOf ('.');
3076 return name.Substring (i+1);
3079 public static void GetInfoForExplicitlyImplementedMethod (
3080 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
3084 if (method.Overrides.Count != 1)
3085 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
3086 iface = method.Overrides [0].DeclaringType;
3087 ifaceMethod = method.Overrides [0];
3090 public static string GetPropertyName (PropertyDefinition pi)
3092 // Issue: (g)mcs-generated assemblies that explicitly implement
3093 // properties don't specify the full namespace, just the
3094 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
3095 MethodDefinition method = pi.GetMethod;
3097 method = pi.SetMethod;
3098 if (!IsExplicitlyImplemented (method))
3101 // Need to determine appropriate namespace for this member.
3102 TypeReference iface;
3103 MethodReference ifaceMethod;
3104 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3105 return string.Join (".", new string[]{
3106 DocTypeFullMemberFormatter.Default.GetName (iface),
3107 GetMember (pi.Name)});
3110 public static string GetNamespace (TypeReference type)
3112 if (type.GetElementType ().IsNested)
3113 type = type.GetElementType ();
3114 while (type != null && type.IsNested)
3115 type = type.DeclaringType;
3117 return string.Empty;
3119 string typeNS = type.Namespace;
3121 // first, make sure this isn't a type reference to another assembly/module
3123 bool isInAssembly = MDocUpdater.IsInAssemblies(type.Module.Name);
3124 if (isInAssembly && !typeNS.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (type)) {
3125 typeNS = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typeNS);
3130 public static string PathCombine (string dir, string path)
3136 return Path.Combine (dir, path);
3139 public static bool IsExtensionMethod (MethodDefinition method)
3142 method.CustomAttributes
3143 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
3144 && method.DeclaringType.CustomAttributes
3145 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
3148 public static bool IsDelegate (TypeDefinition type)
3150 TypeReference baseRef = type.BaseType;
3151 if (baseRef == null)
3153 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
3154 baseRef.FullName == "System.MulticastDelegate";
3157 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
3159 List<TypeReference> decls = new List<TypeReference> ();
3161 while (type.DeclaringType != null) {
3162 decls.Add (type.DeclaringType);
3163 type = type.DeclaringType;
3169 public static int GetGenericArgumentCount (TypeReference type)
3171 GenericInstanceType inst = type as GenericInstanceType;
3173 ? inst.GenericArguments.Count
3174 : type.GenericParameters.Count;
3177 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
3179 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
3180 List<TypeReference> userInterfaces = new List<TypeReference> ();
3181 foreach (TypeReference iface in type.Interfaces) {
3182 TypeReference lookup = iface.Resolve () ?? iface;
3183 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
3184 userInterfaces.Add (iface);
3186 return userInterfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()));
3189 private static string GetQualifiedTypeName (TypeReference type)
3191 return "[" + type.Scope.Name + "]" + type.FullName;
3194 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
3196 HashSet<string> inheritedInterfaces = new HashSet<string> ();
3197 Action<TypeDefinition> a = null;
3199 if (t == null) return;
3200 foreach (TypeReference r in t.Interfaces) {
3201 inheritedInterfaces.Add (GetQualifiedTypeName (r));
3205 TypeReference baseRef = type.BaseType;
3206 while (baseRef != null) {
3207 TypeDefinition baseDef = baseRef.Resolve ();
3208 if (baseDef != null) {
3210 baseRef = baseDef.BaseType;
3215 foreach (TypeReference r in type.Interfaces)
3217 return inheritedInterfaces;
3221 class DocsNodeInfo {
3222 public DocsNodeInfo (XmlElement node)
3227 public DocsNodeInfo (XmlElement node, TypeDefinition type)
3233 public DocsNodeInfo (XmlElement node, MemberReference member)
3236 SetMemberInfo (member);
3239 void SetType (TypeDefinition type)
3242 throw new ArgumentNullException ("type");
3244 GenericParameters = new List<GenericParameter> (type.GenericParameters);
3245 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
3246 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
3247 for (int i = 0; i < declTypes.Count - 1; ++i) {
3248 int remove = System.Math.Min (maxGenArgs,
3249 DocUtils.GetGenericArgumentCount (declTypes [i]));
3250 maxGenArgs -= remove;
3251 while (remove-- > 0)
3252 GenericParameters.RemoveAt (0);
3254 if (DocUtils.IsDelegate (type)) {
3255 Parameters = type.GetMethod("Invoke").Parameters;
3256 ReturnType = type.GetMethod("Invoke").ReturnType;
3257 ReturnIsReturn = true;
3261 void SetMemberInfo (MemberReference member)
3264 throw new ArgumentNullException ("member");
3265 ReturnIsReturn = true;
3269 if (member is MethodReference ) {
3270 MethodReference mr = (MethodReference) member;
3271 Parameters = mr.Parameters;
3272 if (mr.IsGenericMethod ()) {
3273 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
3276 else if (member is PropertyDefinition) {
3277 Parameters = ((PropertyDefinition) member).Parameters;
3280 if (member is MethodDefinition) {
3281 ReturnType = ((MethodDefinition) member).ReturnType;
3282 } else if (member is PropertyDefinition) {
3283 ReturnType = ((PropertyDefinition) member).PropertyType;
3284 ReturnIsReturn = false;
3287 // no remarks section for enum members
3288 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
3292 public TypeReference ReturnType;
3293 public List<GenericParameter> GenericParameters;
3294 public IList<ParameterDefinition> Parameters;
3295 public bool ReturnIsReturn;
3296 public XmlElement Node;
3297 public bool AddRemarks = true;
3298 public MemberReference Member;
3299 public TypeDefinition Type;
3301 public override string ToString ()
3303 return string.Format ("{0} - {1} - {2}", Type, Member, Node == null ? "no xml" : "with xml");
3307 class DocumentationEnumerator {
3309 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3311 return GetDocumentationTypes (assembly, forTypes, null);
3314 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3316 foreach (TypeDefinition type in assembly.GetTypes()) {
3317 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
3319 if (seen != null && seen.Contains (type.FullName))
3322 foreach (TypeDefinition nested in type.NestedTypes)
3323 yield return nested;
3327 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3329 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
3330 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
3331 oldmember.RemoveAttribute ("__monodocer-seen__");
3334 MemberReference m = GetMember (type, new DocumentationMember (oldmember));
3336 yield return new DocsNodeInfo (oldmember);
3339 yield return new DocsNodeInfo (oldmember, m);
3344 protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
3346 string membertype = member.MemberType;
3348 string returntype = member.ReturnType;
3350 string docName = member.MemberName;
3352 string[] docTypeParams = GetTypeParameters (docName, member.TypeParameters);
3354 // If we're using 'magic types', then we might get false positives ... in those cases, we keep searching
3355 MemberReference likelyCandidate = null;
3357 // Loop through all members in this type with the same name
3358 var reflectedMembers = GetReflectionMembers (type, docName).ToArray ();
3359 foreach (MemberReference mi in reflectedMembers) {
3360 bool matchedMagicType = false;
3361 if (mi is TypeDefinition) continue;
3362 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
3364 if (MDocUpdater.IsPrivate (mi))
3367 IList<ParameterDefinition> pis = null;
3368 string[] typeParams = null;
3369 if (mi is MethodDefinition) {
3370 MethodDefinition mb = (MethodDefinition) mi;
3371 pis = mb.Parameters;
3372 if (mb.IsGenericMethod ()) {
3373 IList<GenericParameter> args = mb.GenericParameters;
3374 typeParams = args.Select (p => p.Name).ToArray ();
3377 else if (mi is PropertyDefinition)
3378 pis = ((PropertyDefinition)mi).Parameters;
3380 // check type parameters
3381 int methodTcount = member.TypeParameters == null ? 0 : member.TypeParameters.Count;
3382 int reflectionTcount = typeParams == null ? 0 : typeParams.Length;
3383 if (methodTcount != reflectionTcount)
3386 // check member parameters
3387 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
3388 int pcount = pis == null ? 0 : pis.Count;
3389 if (mcount != pcount)
3392 MethodDefinition mDef = mi as MethodDefinition;
3393 if (mDef != null && !mDef.IsConstructor) {
3394 // Casting operators can overload based on return type.
3395 string rtype = GetReplacedString (
3396 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
3397 typeParams, docTypeParams);
3398 string originalRType = rtype;
3399 if (MDocUpdater.SwitchingToMagicTypes) {
3400 rtype = NativeTypeManager.ConvertFromNativeType (rtype);
3403 if ((returntype != rtype && originalRType == rtype) ||
3404 (MDocUpdater.SwitchingToMagicTypes && returntype != originalRType && returntype != rtype && originalRType != rtype)) {
3408 if (originalRType != rtype)
3409 matchedMagicType = true;
3415 for (int i = 0; i < pis.Count; i++) {
3416 string paramType = GetReplacedString (
3417 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
3418 typeParams, docTypeParams);
3420 // if magictypes, replace paramType to "classic value" ... so the comparison works
3421 string originalParamType = paramType;
3422 if (MDocUpdater.SwitchingToMagicTypes) {
3423 paramType = NativeTypeManager.ConvertFromNativeType (paramType);
3426 string xmlMemberType = member.Parameters [i];
3427 if ((!paramType.Equals(xmlMemberType) && paramType.Equals(originalParamType)) ||
3428 (MDocUpdater.SwitchingToMagicTypes && !originalParamType.Equals(xmlMemberType) && !paramType.Equals(xmlMemberType) && !paramType.Equals(originalParamType))) {
3430 // did not match ... if we're dropping the namespace, and the paramType has the dropped
3431 // namespace, we should see if it matches when added
3432 bool stillDoesntMatch = true;
3433 if (MDocUpdater.HasDroppedNamespace(type) && paramType.StartsWith (MDocUpdater.droppedNamespace)) {
3434 string withDroppedNs = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, xmlMemberType);
3436 stillDoesntMatch = withDroppedNs != paramType;
3439 if (stillDoesntMatch) {
3445 if (originalParamType != paramType)
3446 matchedMagicType = true;
3448 if (!good) continue;
3450 if (MDocUpdater.SwitchingToMagicTypes && likelyCandidate == null && matchedMagicType) {
3451 // 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
3452 likelyCandidate = mi;
3459 return likelyCandidate;
3462 static string[] GetTypeParameters (string docName, IEnumerable<string> knownParameters)
3464 if (docName [docName.Length-1] != '>')
3466 StringList types = new StringList ();
3467 int endToken = docName.Length-2;
3468 int i = docName.Length-2;
3470 if (docName [i] == ',' || docName [i] == '<') {
3471 types.Add (docName.Substring (i + 1, endToken - i));
3474 if (docName [i] == '<')
3479 var arrayTypes = types.ToArray ();
3481 if (knownParameters != null && knownParameters.Any () && arrayTypes.Length != knownParameters.Count ())
3482 return knownParameters.ToArray ();
3487 protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
3489 // In case of dropping the namespace, we have to remove the dropped NS
3490 // so that docName will match what's in the assembly/type
3491 if (MDocUpdater.HasDroppedNamespace (type) && docName.StartsWith(MDocUpdater.droppedNamespace + ".")) {
3492 int droppedNsLength = MDocUpdater.droppedNamespace.Length;
3493 docName = docName.Substring (droppedNsLength + 1, docName.Length - droppedNsLength - 1);
3496 // need to worry about 4 forms of //@MemberName values:
3497 // 1. "Normal" (non-generic) member names: GetEnumerator
3499 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
3500 // - try as-is, and try type.member (due to "kludge" for property
3502 // 3. "Normal" Generic member names: Sort<T> (CSC)
3503 // - need to remove generic parameters --> "Sort"
3504 // 4. Explicitly-implemented interface members for generic interfaces:
3505 // -- System.Collections.Generic.IEnumerable<T>.Current
3506 // - Try as-is, and try type.member, *keeping* the generic parameters.
3507 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
3508 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
3509 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
3510 // this as (1) or (2).
3511 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
3513 foreach (MemberReference mi in type.GetMembers (docName))
3515 if (CountChars (docName, '.') > 0)
3516 // might be a property; try only type.member instead of
3517 // namespace.type.member.
3518 foreach (MemberReference mi in
3519 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
3526 int startLt, startType, startMethod;
3527 startLt = startType = startMethod = -1;
3528 for (int i = 0; i < docName.Length; ++i) {
3529 switch (docName [i]) {
3538 if (numLt == 0 && (i + 1) < docName.Length)
3539 // there's another character in docName, so this <...> sequence is
3540 // probably part of a generic type -- case 4.
3544 startType = startMethod;
3550 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
3552 foreach (MemberReference mi in type.GetMembers (refName))
3556 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
3559 // If we _still_ haven't found it, we've hit another generic naming issue:
3560 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
3561 // explicitly-implemented METHOD names (not properties), e.g.
3562 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
3563 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
3564 // which the XML docs will contain.
3566 // Alas, we can't derive the Mono name from docName, so we need to iterate
3567 // over all member names, convert them into CSC format, and compare... :-(
3570 foreach (MemberReference mi in type.GetMembers ()) {
3571 if (MDocUpdater.GetMemberName (mi) == docName)
3576 static string GetReplacedString (string typeName, string[] from, string[] to)
3580 for (int i = 0; i < from.Length; ++i)
3581 typeName = typeName.Replace (from [i], to [i]);
3585 private static int CountChars (string s, char c)
3588 for (int i = 0; i < s.Length; ++i) {
3596 class EcmaDocumentationEnumerator : DocumentationEnumerator {
3601 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
3604 this.ecmadocs = ecmaDocs;
3607 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3609 HashSet<string> seen = new HashSet<string> ();
3610 return GetDocumentationTypes (assembly, forTypes, seen)
3611 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
3614 new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3617 while (ecmadocs.Read ()) {
3618 switch (ecmadocs.Name) {
3620 if (typeDepth == -1)
3621 typeDepth = ecmadocs.Depth;
3622 if (ecmadocs.NodeType != XmlNodeType.Element)
3624 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
3626 string typename = ecmadocs.GetAttribute ("FullName");
3627 string typename2 = MDocUpdater.GetTypeFileName (typename);
3628 if (forTypes != null &&
3629 forTypes.BinarySearch (typename) < 0 &&
3630 typename != typename2 &&
3631 forTypes.BinarySearch (typename2) < 0)
3634 if ((t = assembly.GetType (typename)) == null &&
3635 (t = assembly.GetType (typename2)) == null)
3637 seen.Add (typename);
3638 if (typename != typename2)
3639 seen.Add (typename2);
3640 Console.WriteLine (" Import: {0}", t.FullName);
3641 if (ecmadocs.Name != "Docs") {
3642 int depth = ecmadocs.Depth;
3643 while (ecmadocs.Read ()) {
3644 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
3648 if (!ecmadocs.IsStartElement ("Docs"))
3649 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
3659 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3661 return GetMembers (basefile, type)
3662 .Concat (base.GetDocumentationMembers (basefile, type));
3665 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
3667 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
3670 if (ecmadocs.IsEmptyElement)
3673 int membersDepth = ecmadocs.Depth;
3675 while (go && ecmadocs.Read ()) {
3676 switch (ecmadocs.Name) {
3678 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
3680 DocumentationMember dm = new DocumentationMember (ecmadocs);
3682 string xp = MDocUpdater.GetXPathForMember (dm);
3683 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
3685 if (oldmember == null) {
3686 m = GetMember (type, dm);
3688 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3689 type.FullName, dm.MemberSignatures ["C#"]);
3690 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
3693 // oldmember lookup may have failed due to type parameter renames.
3695 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
3696 if (oldmember == null) {
3697 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
3698 oldmember = basefile.CreateElement ("Member");
3699 oldmember.SetAttribute ("MemberName", dm.MemberName);
3700 members.AppendChild (oldmember);
3701 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
3702 XmlElement ms = basefile.CreateElement ("MemberSignature");
3703 ms.SetAttribute ("Language", key);
3704 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
3705 oldmember.AppendChild (ms);
3707 oldmember.SetAttribute ("__monodocer-seen__", "true");
3708 Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
3713 m = GetMember (type, new DocumentationMember (oldmember));
3715 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3716 type.FullName, dm.MemberSignatures ["C#"]);
3719 oldmember.SetAttribute ("__monodocer-seen__", "true");
3721 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
3722 if (ecmadocs.Name != "Docs")
3723 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
3728 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
3737 abstract class DocumentationImporter {
3739 public abstract void ImportDocumentation (DocsNodeInfo info);
3742 class MsxdocDocumentationImporter : DocumentationImporter {
3744 XmlDocument slashdocs;
3746 public MsxdocDocumentationImporter (string file)
3748 var xml = File.ReadAllText (file);
3750 // Ensure Unix line endings
3751 xml = xml.Replace ("\r", "");
3753 slashdocs = new XmlDocument();
3754 slashdocs.LoadXml (xml);
3757 public override void ImportDocumentation (DocsNodeInfo info)
3759 XmlNode elem = GetDocs (info.Member ?? info.Type);
3764 XmlElement e = info.Node;
3766 if (elem.SelectSingleNode("summary") != null)
3767 MDocUpdater.ClearElement(e, "summary");
3768 if (elem.SelectSingleNode("remarks") != null)
3769 MDocUpdater.ClearElement(e, "remarks");
3770 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3771 MDocUpdater.ClearElement(e, "value");
3772 MDocUpdater.ClearElement(e, "returns");
3775 foreach (XmlNode child in elem.ChildNodes) {
3776 switch (child.Name) {
3779 XmlAttribute name = child.Attributes ["name"];
3782 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3784 p2.InnerXml = child.InnerXml;
3787 // Occasionally XML documentation will use <returns/> on
3788 // properties, so let's try to normalize things.
3791 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3792 v.InnerXml = child.InnerXml;
3798 case "permission": {
3799 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3802 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3804 a = e.OwnerDocument.CreateElement (child.Name);
3805 a.SetAttribute ("cref", cref.Value);
3808 a.InnerXml = child.InnerXml;
3812 XmlAttribute cref = child.Attributes ["cref"];
3815 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3817 a = e.OwnerDocument.CreateElement ("altmember");
3818 a.SetAttribute ("cref", cref.Value);
3825 if (child.NodeType == XmlNodeType.Element &&
3826 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3829 MDocUpdater.CopyNode (child, e);
3836 private XmlNode GetDocs (MemberReference member)
3838 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3839 if (slashdocsig != null)
3840 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3845 class EcmaDocumentationImporter : DocumentationImporter {
3849 public EcmaDocumentationImporter (XmlReader ecmaDocs)
3851 this.ecmadocs = ecmaDocs;
3854 public override void ImportDocumentation (DocsNodeInfo info)
3856 if (!ecmadocs.IsStartElement ("Docs")) {
3860 XmlElement e = info.Node;
3862 int depth = ecmadocs.Depth;
3863 ecmadocs.ReadStartElement ("Docs");
3864 while (ecmadocs.Read ()) {
3865 if (ecmadocs.Name == "Docs") {
3866 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3869 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3871 if (!ecmadocs.IsStartElement ())
3873 switch (ecmadocs.Name) {
3876 string name = ecmadocs.GetAttribute ("name");
3879 XmlNode doc = e.SelectSingleNode (
3880 ecmadocs.Name + "[@name='" + name + "']");
3881 string value = ecmadocs.ReadInnerXml ();
3883 doc.InnerXml = value.Replace ("\r", "");
3890 string name = ecmadocs.Name;
3891 string cref = ecmadocs.GetAttribute ("cref");
3894 XmlNode doc = e.SelectSingleNode (
3895 ecmadocs.Name + "[@cref='" + cref + "']");
3896 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3898 doc.InnerXml = value;
3900 XmlElement n = e.OwnerDocument.CreateElement (name);
3901 n.SetAttribute ("cref", cref);
3908 string name = ecmadocs.Name;
3909 string xpath = ecmadocs.Name;
3910 StringList attributes = new StringList (ecmadocs.AttributeCount);
3911 if (ecmadocs.MoveToFirstAttribute ()) {
3913 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3914 } while (ecmadocs.MoveToNextAttribute ());
3915 ecmadocs.MoveToContent ();
3917 if (attributes.Count > 0) {
3918 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3920 XmlNode doc = e.SelectSingleNode (xpath);
3921 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3923 doc.InnerXml = value;
3926 XmlElement n = e.OwnerDocument.CreateElement (name);
3928 foreach (string a in attributes) {
3929 int eq = a.IndexOf ('=');
3930 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3941 class DocumentationMember {
3942 public StringToStringMap MemberSignatures = new StringToStringMap ();
3943 public string ReturnType;
3944 public StringList Parameters;
3945 public StringList TypeParameters;
3946 public string MemberName;
3947 public string MemberType;
3949 public DocumentationMember (XmlReader reader)
3951 MemberName = reader.GetAttribute ("MemberName");
3952 int depth = reader.Depth;
3954 StringList p = new StringList ();
3955 StringList tp = new StringList ();
3957 if (reader.NodeType != XmlNodeType.Element)
3960 bool shouldUse = true;
3962 string apistyle = reader.GetAttribute ("apistyle");
3963 shouldUse = string.IsNullOrWhiteSpace(apistyle) || apistyle == "classic"; // only use this tag if it's an 'classic' style node
3965 catch (Exception ex) {}
3966 switch (reader.Name) {
3967 case "MemberSignature":
3969 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3973 MemberType = reader.ReadElementString ();
3976 if (reader.Depth == depth + 2 && shouldUse)
3977 ReturnType = reader.ReadElementString ();
3980 if (reader.Depth == depth + 2 && shouldUse)
3981 p.Add (reader.GetAttribute ("Type"));
3983 case "TypeParameter":
3984 if (reader.Depth == depth + 2 && shouldUse)
3985 tp.Add (reader.GetAttribute ("Name"));
3988 if (reader.Depth == depth + 1)
3992 } while (go && reader.Read () && reader.Depth >= depth);
3997 TypeParameters = tp;
3999 DiscernTypeParameters ();
4003 public DocumentationMember (XmlNode node)
4005 MemberName = node.Attributes ["MemberName"].Value;
4006 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
4007 XmlAttribute l = n.Attributes ["Language"];
4008 XmlAttribute v = n.Attributes ["Value"];
4009 XmlAttribute apistyle = n.Attributes ["apistyle"];
4010 bool shouldUse = apistyle == null || apistyle.Value == "classic";
4011 if (l != null && v != null && shouldUse)
4012 MemberSignatures [l.Value] = v.Value;
4014 MemberType = node.SelectSingleNode ("MemberType").InnerText;
4015 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType[not(@apistyle) or @apistyle='classic']");
4017 ReturnType = rt.InnerText;
4018 XmlNodeList p = node.SelectNodes ("Parameters/Parameter[not(@apistyle) or @apistyle='classic']");
4020 Parameters = new StringList (p.Count);
4021 for (int i = 0; i < p.Count; ++i)
4022 Parameters.Add (p [i].Attributes ["Type"].Value);
4024 XmlNodeList tp = node.SelectNodes ("TypeParameters/TypeParameter[not(@apistyle) or @apistyle='classic']");
4026 TypeParameters = new StringList (tp.Count);
4027 for (int i = 0; i < tp.Count; ++i)
4028 TypeParameters.Add (tp [i].Attributes ["Name"].Value);
4031 DiscernTypeParameters ();
4035 void DiscernTypeParameters ()
4037 // see if we can discern the param list from the name
4038 if (MemberName.Contains ("<") && MemberName.EndsWith (">")) {
4039 var starti = MemberName.IndexOf ("<") + 1;
4040 var endi = MemberName.LastIndexOf (">");
4041 var paramlist = MemberName.Substring (starti, endi - starti);
4042 var tparams = paramlist.Split (new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
4043 TypeParameters = new StringList (tparams);
4048 public class DynamicParserContext {
4049 public ReadOnlyCollection<bool> TransformFlags;
4050 public int TransformIndex;
4052 public DynamicParserContext (ICustomAttributeProvider provider)
4055 if (provider.HasCustomAttributes &&
4056 (da = (provider.CustomAttributes.Cast<CustomAttribute>()
4057 .SingleOrDefault (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))) != null) {
4058 CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
4059 ? new CustomAttributeArgument [0]
4060 : (CustomAttributeArgument[]) da.ConstructorArguments [0].Value;
4062 TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool) t.Value).ToArray());
4067 public enum MemberFormatterState {
4069 WithinGenericTypeParameters,
4072 public abstract class MemberFormatter {
4074 public virtual string Language {
4078 public string GetName (MemberReference member)
4080 return GetName (member, null);
4083 public virtual string GetName (MemberReference member, DynamicParserContext context)
4085 TypeReference type = member as TypeReference;
4087 return GetTypeName (type, context);
4088 MethodReference method = member as MethodReference;
4089 if (method != null && method.Name == ".ctor") // method.IsConstructor
4090 return GetConstructorName (method);
4092 return GetMethodName (method);
4093 PropertyReference prop = member as PropertyReference;
4095 return GetPropertyName (prop);
4096 FieldReference field = member as FieldReference;
4098 return GetFieldName (field);
4099 EventReference e = member as EventReference;
4101 return GetEventName (e);
4102 throw new NotSupportedException ("Can't handle: " +
4103 (member == null ? "null" : member.GetType().ToString()));
4106 protected virtual string GetTypeName (TypeReference type)
4108 return GetTypeName (type, null);
4111 protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
4114 throw new ArgumentNullException ("type");
4115 return _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
4118 protected virtual char[] ArrayDelimeters {
4119 get {return new char[]{'[', ']'};}
4122 protected virtual MemberFormatterState MemberFormatterState { get; set; }
4124 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4126 if (type is ArrayType) {
4127 TypeSpecification spec = type as TypeSpecification;
4128 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
4129 return AppendArrayModifiers (buf, (ArrayType) type);
4131 if (type is ByReferenceType) {
4132 return AppendRefTypeName (buf, type, context);
4134 if (type is PointerType) {
4135 return AppendPointerTypeName (buf, type, context);
4137 if (type is GenericParameter) {
4138 return AppendTypeName (buf, type, context);
4140 AppendNamespace (buf, type);
4141 GenericInstanceType genInst = type as GenericInstanceType;
4142 if (type.GenericParameters.Count == 0 &&
4143 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
4144 return AppendFullTypeName (buf, type, context);
4146 return AppendGenericType (buf, type, context);
4149 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4151 string ns = DocUtils.GetNamespace (type);
4152 if (ns != null && ns.Length > 0)
4153 buf.Append (ns).Append ('.');
4157 protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4159 if (type.DeclaringType != null)
4160 AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
4161 return AppendTypeName (buf, type, context);
4164 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4166 if (context != null)
4167 context.TransformIndex++;
4168 return AppendTypeName (buf, type.Name);
4171 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
4173 int n = typename.IndexOf ("`");
4175 return buf.Append (typename.Substring (0, n));
4176 return buf.Append (typename);
4179 protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
4181 buf.Append (ArrayDelimeters [0]);
4182 int rank = array.Rank;
4184 buf.Append (new string (',', rank-1));
4185 return buf.Append (ArrayDelimeters [1]);
4188 protected virtual string RefTypeModifier {
4192 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4194 TypeSpecification spec = type as TypeSpecification;
4195 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4196 .Append (RefTypeModifier);
4199 protected virtual string PointerModifier {
4203 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4205 TypeSpecification spec = type as TypeSpecification;
4206 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4207 .Append (PointerModifier);
4210 protected virtual char[] GenericTypeContainer {
4211 get {return new char[]{'<', '>'};}
4214 protected virtual char NestedTypeSeparator {
4218 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4220 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4221 type is GenericInstanceType ? type.GetElementType () : type);
4222 List<TypeReference> genArgs = GetGenericArguments (type);
4225 bool insertNested = false;
4226 foreach (var decl in decls) {
4227 TypeReference declDef = decl.Resolve () ?? decl;
4229 buf.Append (NestedTypeSeparator);
4231 insertNested = true;
4232 AppendTypeName (buf, declDef, context);
4233 int ac = DocUtils.GetGenericArgumentCount (declDef);
4237 buf.Append (GenericTypeContainer [0]);
4238 var origState = MemberFormatterState;
4239 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4240 _AppendTypeName (buf, genArgs [argIdx++], context);
4241 for (int i = 1; i < c; ++i) {
4242 _AppendTypeName (buf.Append (","), genArgs [argIdx++], context);
4244 MemberFormatterState = origState;
4245 buf.Append (GenericTypeContainer [1]);
4251 protected List<TypeReference> GetGenericArguments (TypeReference type)
4253 var args = new List<TypeReference> ();
4254 GenericInstanceType inst = type as GenericInstanceType;
4256 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
4258 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
4262 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4267 protected virtual string GetConstructorName (MethodReference constructor)
4269 return constructor.Name;
4272 protected virtual string GetMethodName (MethodReference method)
4277 protected virtual string GetPropertyName (PropertyReference property)
4279 return property.Name;
4282 protected virtual string GetFieldName (FieldReference field)
4287 protected virtual string GetEventName (EventReference e)
4292 public virtual string GetDeclaration (MemberReference member)
4295 throw new ArgumentNullException ("member");
4296 TypeDefinition type = member as TypeDefinition;
4298 return GetTypeDeclaration (type);
4299 MethodDefinition method = member as MethodDefinition;
4300 if (method != null && method.IsConstructor)
4301 return GetConstructorDeclaration (method);
4303 return GetMethodDeclaration (method);
4304 PropertyDefinition prop = member as PropertyDefinition;
4306 return GetPropertyDeclaration (prop);
4307 FieldDefinition field = member as FieldDefinition;
4309 return GetFieldDeclaration (field);
4310 EventDefinition e = member as EventDefinition;
4312 return GetEventDeclaration (e);
4313 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
4316 protected virtual string GetTypeDeclaration (TypeDefinition type)
4319 throw new ArgumentNullException ("type");
4320 StringBuilder buf = new StringBuilder (type.Name.Length);
4321 _AppendTypeName (buf, type, null);
4322 AppendGenericTypeConstraints (buf, type);
4323 return buf.ToString ();
4326 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
4328 return GetConstructorName (constructor);
4331 protected virtual string GetMethodDeclaration (MethodDefinition method)
4333 if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
4334 ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
4337 // Special signature for destructors.
4338 if (method.Name == "Finalize" && method.Parameters.Count == 0)
4339 return GetFinalizerName (method);
4341 StringBuilder buf = new StringBuilder ();
4343 AppendVisibility (buf, method);
4344 if (buf.Length == 0 &&
4345 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
4348 AppendModifiers (buf, method);
4350 if (buf.Length != 0)
4352 buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
4354 AppendMethodName (buf, method);
4355 AppendGenericMethod (buf, method).Append (" ");
4356 AppendParameters (buf, method, method.Parameters);
4357 AppendGenericMethodConstraints (buf, method);
4358 return buf.ToString ();
4361 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4363 return buf.Append (method.Name);
4366 protected virtual string GetFinalizerName (MethodDefinition method)
4371 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4376 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4381 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4386 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4391 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4396 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
4398 return GetPropertyName (property);
4401 protected virtual string GetFieldDeclaration (FieldDefinition field)
4403 return GetFieldName (field);
4406 protected virtual string GetEventDeclaration (EventDefinition e)
4408 return GetEventName (e);
4412 class ILFullMemberFormatter : MemberFormatter {
4414 public override string Language {
4415 get {return "ILAsm";}
4418 protected override char NestedTypeSeparator {
4424 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4426 if (GetBuiltinType (type.FullName) != null)
4428 string ns = DocUtils.GetNamespace (type);
4429 if (ns != null && ns.Length > 0) {
4430 if (type.IsValueType)
4431 buf.Append ("valuetype ");
4433 buf.Append ("class ");
4434 buf.Append (ns).Append ('.');
4439 protected static string GetBuiltinType (string t)
4442 case "System.Byte": return "unsigned int8";
4443 case "System.SByte": return "int8";
4444 case "System.Int16": return "int16";
4445 case "System.Int32": return "int32";
4446 case "System.Int64": return "int64";
4447 case "System.IntPtr": return "native int";
4449 case "System.UInt16": return "unsigned int16";
4450 case "System.UInt32": return "unsigned int32";
4451 case "System.UInt64": return "unsigned int64";
4452 case "System.UIntPtr": return "native unsigned int";
4454 case "System.Single": return "float32";
4455 case "System.Double": return "float64";
4456 case "System.Boolean": return "bool";
4457 case "System.Char": return "char";
4458 case "System.Void": return "void";
4459 case "System.String": return "string";
4460 case "System.Object": return "object";
4465 protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
4467 return buf.Append (typename);
4470 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4472 if (type is GenericParameter) {
4473 AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
4477 string s = GetBuiltinType (type.FullName);
4479 return buf.Append (s);
4481 return base.AppendTypeName (buf, type, context);
4484 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
4486 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
4487 return buf.Append (type.Owner is TypeReference ? "!" : "!!");
4489 GenericParameterAttributes attrs = type.Attributes;
4490 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
4491 buf.Append ("class ");
4492 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
4493 buf.Append ("struct ");
4494 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
4495 buf.Append (".ctor ");
4496 IList<TypeReference> constraints = type.Constraints;
4497 MemberFormatterState = 0;
4498 if (constraints.Count > 0) {
4499 var full = new ILFullMemberFormatter ();
4500 buf.Append ("(").Append (full.GetName (constraints [0]));
4501 for (int i = 1; i < constraints.Count; ++i) {
4502 buf.Append (", ").Append (full.GetName (constraints [i]));
4506 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4508 if ((attrs & GenericParameterAttributes.Covariant) != 0)
4510 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
4515 protected override string GetTypeDeclaration (TypeDefinition type)
4517 string visibility = GetTypeVisibility (type.Attributes);
4518 if (visibility == null)
4521 StringBuilder buf = new StringBuilder ();
4523 buf.Append (".class ");
4525 buf.Append ("nested ");
4526 buf.Append (visibility).Append (" ");
4527 if (type.IsInterface)
4528 buf.Append ("interface ");
4529 if (type.IsSequentialLayout)
4530 buf.Append ("sequential ");
4531 if (type.IsAutoLayout)
4532 buf.Append ("auto ");
4533 if (type.IsAnsiClass)
4534 buf.Append ("ansi ");
4535 if (type.IsAbstract)
4536 buf.Append ("abstract ");
4537 if (type.IsSerializable)
4538 buf.Append ("serializable ");
4540 buf.Append ("sealed ");
4541 if (type.IsBeforeFieldInit)
4542 buf.Append ("beforefieldinit ");
4543 var state = MemberFormatterState;
4544 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4545 buf.Append (GetName (type));
4546 MemberFormatterState = state;
4547 var full = new ILFullMemberFormatter ();
4548 if (type.BaseType != null) {
4549 buf.Append (" extends ");
4550 if (type.BaseType.FullName == "System.Object")
4551 buf.Append ("System.Object");
4553 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
4556 foreach (var name in type.Interfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()))
4557 .Select (i => full.GetName (i))
4558 .OrderBy (n => n)) {
4560 buf.Append (" implements ");
4569 return buf.ToString ();
4572 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4574 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4575 type is GenericInstanceType ? type.GetElementType () : type);
4577 foreach (var decl in decls) {
4578 TypeReference declDef = decl.Resolve () ?? decl;
4580 buf.Append (NestedTypeSeparator);
4583 AppendTypeName (buf, declDef, context);
4587 foreach (TypeReference arg in GetGenericArguments (type)) {
4591 _AppendTypeName (buf, arg, context);
4597 static string GetTypeVisibility (TypeAttributes ta)
4599 switch (ta & TypeAttributes.VisibilityMask) {
4600 case TypeAttributes.Public:
4601 case TypeAttributes.NestedPublic:
4604 case TypeAttributes.NestedFamily:
4605 case TypeAttributes.NestedFamORAssem:
4613 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4615 return GetMethodDeclaration (constructor);
4618 protected override string GetMethodDeclaration (MethodDefinition method)
4620 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
4623 var buf = new StringBuilder ();
4624 buf.Append (".method ");
4625 AppendVisibility (buf, method);
4626 if (method.IsStatic)
4627 buf.Append ("static ");
4628 if (method.IsHideBySig)
4629 buf.Append ("hidebysig ");
4630 if (method.IsPInvokeImpl) {
4631 var info = method.PInvokeInfo;
4632 buf.Append ("pinvokeimpl (\"")
4633 .Append (info.Module.Name)
4634 .Append ("\" as \"")
4635 .Append (info.EntryPoint)
4637 if (info.IsCharSetAuto)
4638 buf.Append (" auto");
4639 if (info.IsCharSetUnicode)
4640 buf.Append (" unicode");
4641 if (info.IsCharSetAnsi)
4642 buf.Append (" ansi");
4643 if (info.IsCallConvCdecl)
4644 buf.Append (" cdecl");
4645 if (info.IsCallConvStdCall)
4646 buf.Append (" stdcall");
4647 if (info.IsCallConvWinapi)
4648 buf.Append (" winapi");
4649 if (info.IsCallConvThiscall)
4650 buf.Append (" thiscall");
4651 if (info.SupportsLastError)
4652 buf.Append (" lasterr");
4655 if (method.IsSpecialName)
4656 buf.Append ("specialname ");
4657 if (method.IsRuntimeSpecialName)
4658 buf.Append ("rtspecialname ");
4659 if (method.IsNewSlot)
4660 buf.Append ("newslot ");
4661 if (method.IsVirtual)
4662 buf.Append ("virtual ");
4663 if (!method.IsStatic)
4664 buf.Append ("instance ");
4665 _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
4667 .Append (method.Name);
4668 if (method.IsGenericMethod ()) {
4669 var state = MemberFormatterState;
4670 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4671 IList<GenericParameter> args = method.GenericParameters;
4672 if (args.Count > 0) {
4674 _AppendTypeName (buf, args [0], null);
4675 for (int i = 1; i < args.Count; ++i)
4676 _AppendTypeName (buf.Append (", "), args [i], null);
4679 MemberFormatterState = state;
4684 for (int i = 0; i < method.Parameters.Count; ++i) {
4688 _AppendTypeName (buf, method.Parameters [i].ParameterType, new DynamicParserContext (method.Parameters [i]));
4690 buf.Append (method.Parameters [i].Name);
4694 buf.Append (" cil");
4695 if (method.IsRuntime)
4696 buf.Append (" runtime");
4697 if (method.IsManaged)
4698 buf.Append (" managed");
4700 return buf.ToString ();
4703 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4705 if (DocUtils.IsExplicitlyImplemented (method)) {
4706 TypeReference iface;
4707 MethodReference ifaceMethod;
4708 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4709 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4711 .Append (ifaceMethod.Name);
4713 return base.AppendMethodName (buf, method);
4716 protected override string RefTypeModifier {
4720 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4722 if (method.IsPublic)
4723 return buf.Append ("public ");
4724 if (method.IsFamilyAndAssembly)
4725 return buf.Append ("familyandassembly");
4726 if (method.IsFamilyOrAssembly)
4727 return buf.Append ("familyorassembly");
4728 if (method.IsFamily)
4729 return buf.Append ("family");
4733 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4735 string modifiers = String.Empty;
4736 if (method.IsStatic) modifiers += " static";
4737 if (method.IsVirtual && !method.IsAbstract) {
4738 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4739 else modifiers += " override";
4741 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4742 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4743 if (method.IsFinal) modifiers += " sealed";
4744 if (modifiers == " virtual sealed") modifiers = "";
4746 return buf.Append (modifiers);
4749 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4751 if (method.IsGenericMethod ()) {
4752 IList<GenericParameter> args = method.GenericParameters;
4753 if (args.Count > 0) {
4755 buf.Append (args [0].Name);
4756 for (int i = 1; i < args.Count; ++i)
4757 buf.Append (",").Append (args [i].Name);
4764 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4766 return AppendParameters (buf, method, parameters, '(', ')');
4769 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4773 if (parameters.Count > 0) {
4774 if (DocUtils.IsExtensionMethod (method))
4775 buf.Append ("this ");
4776 AppendParameter (buf, parameters [0]);
4777 for (int i = 1; i < parameters.Count; ++i) {
4779 AppendParameter (buf, parameters [i]);
4783 return buf.Append (end);
4786 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4788 if (parameter.ParameterType is ByReferenceType) {
4789 if (parameter.IsOut)
4790 buf.Append ("out ");
4792 buf.Append ("ref ");
4794 buf.Append (GetName (parameter.ParameterType)).Append (" ");
4795 return buf.Append (parameter.Name);
4798 protected override string GetPropertyDeclaration (PropertyDefinition property)
4800 MethodDefinition gm = null, sm = null;
4802 string get_visible = null;
4803 if ((gm = property.GetMethod) != null &&
4804 (DocUtils.IsExplicitlyImplemented (gm) ||
4805 (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
4806 get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
4807 string set_visible = null;
4808 if ((sm = property.SetMethod) != null &&
4809 (DocUtils.IsExplicitlyImplemented (sm) ||
4810 (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
4811 set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
4813 if ((set_visible == null) && (get_visible == null))
4816 StringBuilder buf = new StringBuilder ()
4817 .Append (".property ");
4818 if (!(gm ?? sm).IsStatic)
4819 buf.Append ("instance ");
4820 _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
4821 buf.Append (' ').Append (property.Name);
4822 if (!property.HasParameters || property.Parameters.Count == 0)
4823 return buf.ToString ();
4827 foreach (ParameterDefinition p in property.Parameters) {
4831 _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
4835 return buf.ToString ();
4838 protected override string GetFieldDeclaration (FieldDefinition field)
4840 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4841 if (declType.IsEnum && field.Name == "value__")
4842 return null; // This member of enums aren't documented.
4844 StringBuilder buf = new StringBuilder ();
4845 AppendFieldVisibility (buf, field);
4846 if (buf.Length == 0)
4849 buf.Insert (0, ".field ");
4852 buf.Append ("static ");
4853 if (field.IsInitOnly)
4854 buf.Append ("initonly ");
4855 if (field.IsLiteral)
4856 buf.Append ("literal ");
4857 _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
4858 buf.Append (' ').Append (field.Name);
4859 AppendFieldValue (buf, field);
4861 return buf.ToString ();
4864 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4867 return buf.Append ("public ");
4868 if (field.IsFamilyAndAssembly)
4869 return buf.Append ("familyandassembly ");
4870 if (field.IsFamilyOrAssembly)
4871 return buf.Append ("familyorassembly ");
4873 return buf.Append ("family ");
4877 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4879 // enums have a value__ field, which we ignore
4880 if (field.DeclaringType.IsGenericType ())
4882 if (field.HasConstant && field.IsLiteral) {
4885 val = field.Constant;
4890 buf.Append (" = ").Append ("null");
4891 else if (val is Enum)
4893 .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4895 .Append (val.ToString ())
4897 else if (val is IFormattable) {
4898 string value = ((IFormattable)val).ToString();
4901 buf.Append ("\"" + value + "\"");
4903 buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4912 protected override string GetEventDeclaration (EventDefinition e)
4914 StringBuilder buf = new StringBuilder ();
4915 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4920 buf.Append (".event ")
4921 .Append (GetName (e.EventType))
4925 return buf.ToString ();
4929 class ILMemberFormatter : ILFullMemberFormatter {
4930 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4936 class ILNativeTypeMemberFormatter : ILFullMemberFormatter {
4937 protected static string _GetBuiltinType (string t)
4939 //string moddedType = base.GetBuiltinType (t);
4941 //return moddedType;
4945 class CSharpNativeTypeMemberFormatter : CSharpFullMemberFormatter {
4946 protected override string GetCSharpType (string t) {
4947 string moddedType = base.GetCSharpType (t);
4949 switch (moddedType) {
4950 case "int": return "nint";
4955 case "System.Drawing.SizeF":
4956 return "CoreGraphics.CGSize";
4957 case "System.Drawing.PointF":
4958 return "CoreGraphics.CGPoint";
4959 case "System.Drawing.RectangleF":
4960 return "CoreGraphics.CGPoint";
4966 class CSharpFullMemberFormatter : MemberFormatter {
4968 public override string Language {
4972 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4975 string ns = DocUtils.GetNamespace (type);
4976 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
4977 buf.Append (ns).Append ('.');
4981 protected virtual string GetCSharpType (string t)
4984 case "System.Byte": return "byte";
4985 case "System.SByte": return "sbyte";
4986 case "System.Int16": return "short";
4987 case "System.Int32": return "int";
4988 case "System.Int64": return "long";
4990 case "System.UInt16": return "ushort";
4991 case "System.UInt32": return "uint";
4992 case "System.UInt64": return "ulong";
4994 case "System.Single": return "float";
4995 case "System.Double": return "double";
4996 case "System.Decimal": return "decimal";
4997 case "System.Boolean": return "bool";
4998 case "System.Char": return "char";
4999 case "System.Void": return "void";
5000 case "System.String": return "string";
5001 case "System.Object": return "object";
5006 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
5008 if (context != null && context.TransformFlags != null &&
5009 (context.TransformFlags.Count == 0 || context.TransformFlags [context.TransformIndex])) {
5010 context.TransformIndex++;
5011 return buf.Append ("dynamic");
5014 if (type is GenericParameter)
5015 return AppendGenericParameterConstraints (buf, (GenericParameter) type, context).Append (type.Name);
5016 string t = type.FullName;
5017 if (!t.StartsWith ("System.")) {
5018 return base.AppendTypeName (buf, type, context);
5021 string s = GetCSharpType (t);
5023 if (context != null)
5024 context.TransformIndex++;
5025 return buf.Append (s);
5028 return base.AppendTypeName (buf, type, context);
5031 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
5033 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
5035 GenericParameterAttributes attrs = type.Attributes;
5036 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
5037 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
5041 buf.Append ("out ");
5045 protected override string GetTypeDeclaration (TypeDefinition type)
5047 string visibility = GetTypeVisibility (type.Attributes);
5048 if (visibility == null)
5051 StringBuilder buf = new StringBuilder ();
5053 buf.Append (visibility);
5056 MemberFormatter full = new CSharpFullMemberFormatter ();
5058 if (DocUtils.IsDelegate (type)) {
5059 buf.Append("delegate ");
5060 MethodDefinition invoke = type.GetMethod ("Invoke");
5061 buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
5062 buf.Append (GetName (type));
5063 AppendParameters (buf, invoke, invoke.Parameters);
5064 AppendGenericTypeConstraints (buf, type);
5067 return buf.ToString();
5070 if (type.IsAbstract && !type.IsInterface)
5071 buf.Append("abstract ");
5072 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
5073 buf.Append("sealed ");
5074 buf.Replace ("abstract sealed", "static");
5076 buf.Append (GetTypeKind (type));
5078 buf.Append (GetCSharpType (type.FullName) == null
5083 TypeReference basetype = type.BaseType;
5084 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
5087 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
5088 .Select (iface => full.GetName (iface))
5092 if (basetype != null || interface_names.Count > 0)
5095 if (basetype != null) {
5096 buf.Append (full.GetName (basetype));
5097 if (interface_names.Count > 0)
5101 for (int i = 0; i < interface_names.Count; i++){
5104 buf.Append (interface_names [i]);
5106 AppendGenericTypeConstraints (buf, type);
5109 return buf.ToString ();
5112 static string GetTypeKind (TypeDefinition t)
5118 if (t.IsClass || t.FullName == "System.Enum")
5122 throw new ArgumentException(t.FullName);
5125 static string GetTypeVisibility (TypeAttributes ta)
5127 switch (ta & TypeAttributes.VisibilityMask) {
5128 case TypeAttributes.Public:
5129 case TypeAttributes.NestedPublic:
5132 case TypeAttributes.NestedFamily:
5133 case TypeAttributes.NestedFamORAssem:
5141 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
5143 if (type.GenericParameters.Count == 0)
5145 return AppendConstraints (buf, type.GenericParameters);
5148 private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
5150 foreach (GenericParameter genArg in genArgs) {
5151 GenericParameterAttributes attrs = genArg.Attributes;
5152 IList<TypeReference> constraints = genArg.Constraints;
5153 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
5156 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
5157 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
5158 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
5161 if (!isref && !isvt && !isnew && constraints.Count == 0)
5163 buf.Append (" where ").Append (genArg.Name).Append (" : ");
5165 buf.Append ("class");
5169 buf.Append ("struct");
5172 if (constraints.Count > 0 && !isvt) {
5175 buf.Append (GetTypeName (constraints [0]));
5176 for (int i = 1; i < constraints.Count; ++i)
5177 buf.Append (", ").Append (GetTypeName (constraints [i]));
5179 if (isnew && !isvt) {
5182 buf.Append ("new()");
5188 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5190 StringBuilder buf = new StringBuilder ();
5191 AppendVisibility (buf, constructor);
5192 if (buf.Length == 0)
5196 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
5197 AppendParameters (buf, constructor, constructor.Parameters);
5200 return buf.ToString ();
5203 protected override string GetMethodDeclaration (MethodDefinition method)
5205 string decl = base.GetMethodDeclaration (method);
5211 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
5213 if (DocUtils.IsExplicitlyImplemented (method)) {
5214 TypeReference iface;
5215 MethodReference ifaceMethod;
5216 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5217 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
5219 .Append (ifaceMethod.Name);
5221 return base.AppendMethodName (buf, method);
5224 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
5226 if (method.GenericParameters.Count == 0)
5228 return AppendConstraints (buf, method.GenericParameters);
5231 protected override string RefTypeModifier {
5235 protected override string GetFinalizerName (MethodDefinition method)
5237 return "~" + method.DeclaringType.Name + " ()";
5240 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
5244 if (method.IsPublic)
5245 return buf.Append ("public");
5246 if (method.IsFamily || method.IsFamilyOrAssembly)
5247 return buf.Append ("protected");
5251 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
5253 string modifiers = String.Empty;
5254 if (method.IsStatic) modifiers += " static";
5255 if (method.IsVirtual && !method.IsAbstract) {
5256 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
5257 else modifiers += " override";
5259 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
5260 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
5261 if (method.IsFinal) modifiers += " sealed";
5262 if (modifiers == " virtual sealed") modifiers = "";
5264 return buf.Append (modifiers);
5267 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
5269 if (method.IsGenericMethod ()) {
5270 IList<GenericParameter> args = method.GenericParameters;
5271 if (args.Count > 0) {
5273 buf.Append (args [0].Name);
5274 for (int i = 1; i < args.Count; ++i)
5275 buf.Append (",").Append (args [i].Name);
5282 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
5284 return AppendParameters (buf, method, parameters, '(', ')');
5287 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
5291 if (parameters.Count > 0) {
5292 if (DocUtils.IsExtensionMethod (method))
5293 buf.Append ("this ");
5294 AppendParameter (buf, parameters [0]);
5295 for (int i = 1; i < parameters.Count; ++i) {
5297 AppendParameter (buf, parameters [i]);
5301 return buf.Append (end);
5304 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
5306 if (parameter.ParameterType is ByReferenceType) {
5307 if (parameter.IsOut)
5308 buf.Append ("out ");
5310 buf.Append ("ref ");
5312 buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
5313 buf.Append (parameter.Name);
5314 if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant) {
5315 buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
5320 protected override string GetPropertyDeclaration (PropertyDefinition property)
5322 MethodDefinition method;
5324 string get_visible = null;
5325 if ((method = property.GetMethod) != null &&
5326 (DocUtils.IsExplicitlyImplemented (method) ||
5327 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5328 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5329 string set_visible = null;
5330 if ((method = property.SetMethod) != null &&
5331 (DocUtils.IsExplicitlyImplemented (method) ||
5332 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5333 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5335 if ((set_visible == null) && (get_visible == null))
5339 StringBuilder buf = new StringBuilder ();
5340 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
5341 buf.Append (visibility = get_visible);
5342 else if (set_visible != null && get_visible == null)
5343 buf.Append (visibility = set_visible);
5345 buf.Append (visibility = "public");
5347 // Pick an accessor to use for static/virtual/override/etc. checks.
5348 method = property.SetMethod;
5350 method = property.GetMethod;
5352 string modifiers = String.Empty;
5353 if (method.IsStatic) modifiers += " static";
5354 if (method.IsVirtual && !method.IsAbstract) {
5355 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
5356 modifiers += " virtual";
5358 modifiers += " override";
5360 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
5361 if (method.IsAbstract && !declDef.IsInterface)
5362 modifiers += " abstract";
5364 modifiers += " sealed";
5365 if (modifiers == " virtual sealed")
5367 buf.Append (modifiers).Append (' ');
5369 buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
5371 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
5372 string name = property.Name;
5373 foreach (MemberReference mi in defs) {
5374 if (mi == property) {
5379 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
5381 if (property.Parameters.Count != 0) {
5382 AppendParameters (buf, method, property.Parameters, '[', ']');
5386 if (get_visible != null) {
5387 if (get_visible != visibility)
5388 buf.Append (' ').Append (get_visible);
5389 buf.Append (" get;");
5391 if (set_visible != null) {
5392 if (set_visible != visibility)
5393 buf.Append (' ').Append (set_visible);
5394 buf.Append (" set;");
5398 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
5401 protected override string GetFieldDeclaration (FieldDefinition field)
5403 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
5404 if (declType.IsEnum && field.Name == "value__")
5405 return null; // This member of enums aren't documented.
5407 StringBuilder buf = new StringBuilder ();
5408 AppendFieldVisibility (buf, field);
5409 if (buf.Length == 0)
5412 if (declType.IsEnum)
5415 if (field.IsStatic && !field.IsLiteral)
5416 buf.Append (" static");
5417 if (field.IsInitOnly)
5418 buf.Append (" readonly");
5419 if (field.IsLiteral)
5420 buf.Append (" const");
5422 buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
5423 buf.Append (field.Name);
5424 AppendFieldValue (buf, field);
5427 return buf.ToString ();
5430 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
5433 return buf.Append ("public");
5434 if (field.IsFamily || field.IsFamilyOrAssembly)
5435 return buf.Append ("protected");
5439 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
5441 // enums have a value__ field, which we ignore
5442 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
5443 field.DeclaringType.IsGenericType ())
5445 if (field.HasConstant && field.IsLiteral) {
5448 val = field.Constant;
5453 buf.Append (" = ").Append ("null");
5454 else if (val is Enum)
5455 buf.Append (" = ").Append (val.ToString ());
5456 else if (val is IFormattable) {
5457 string value = ((IFormattable)val).ToString();
5459 value = "\"" + value + "\"";
5460 buf.Append (" = ").Append (value);
5466 protected override string GetEventDeclaration (EventDefinition e)
5468 StringBuilder buf = new StringBuilder ();
5469 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
5473 AppendModifiers (buf, e.AddMethod);
5475 buf.Append (" event ");
5476 buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters [0]))).Append (' ');
5477 buf.Append (e.Name).Append (';');
5479 return buf.ToString ();
5483 class CSharpMemberFormatter : CSharpFullMemberFormatter {
5484 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5490 class DocTypeFullMemberFormatter : MemberFormatter {
5491 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
5493 protected override char NestedTypeSeparator {
5498 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
5499 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5505 class SlashDocMemberFormatter : MemberFormatter {
5507 protected override char[] GenericTypeContainer {
5508 get {return new char[]{'{', '}'};}
5511 private bool AddTypeCount = true;
5513 private TypeReference genDeclType;
5514 private MethodReference genDeclMethod;
5516 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
5518 if (type is GenericParameter) {
5520 if (genDeclType != null) {
5521 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
5522 for (int i = 0; i < genArgs.Count; ++i) {
5523 if (genArgs [i].Name == type.Name) {
5524 buf.Append ('`').Append (i);
5529 if (genDeclMethod != null) {
5530 IList<GenericParameter> genArgs = null;
5531 if (genDeclMethod.IsGenericMethod ()) {
5532 genArgs = genDeclMethod.GenericParameters;
5533 for (int i = 0; i < genArgs.Count; ++i) {
5534 if (genArgs [i].Name == type.Name) {
5535 buf.Append ("``").Append (i);
5541 if (genDeclType == null && genDeclMethod == null) {
5542 // Probably from within an explicitly implemented interface member,
5543 // where CSC uses parameter names instead of indices (why?), e.g.
5544 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
5545 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
5546 buf.Append (type.Name);
5548 if (buf.Length == l) {
5549 throw new Exception (string.Format (
5550 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
5551 type.Name, genDeclType, genDeclMethod));
5555 base.AppendTypeName (buf, type, context);
5557 int numArgs = type.GenericParameters.Count;
5558 if (type.DeclaringType != null)
5559 numArgs -= type.GenericParameters.Count;
5561 buf.Append ('`').Append (numArgs);
5568 protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
5570 buf.Append (ArrayDelimeters [0]);
5571 int rank = array.Rank;
5574 for (int i = 1; i < rank; ++i) {
5578 return buf.Append (ArrayDelimeters [1]);
5581 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5584 base.AppendGenericType (buf, type, context);
5586 AppendType (buf, type, context);
5590 private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5592 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
5593 bool insertNested = false;
5594 int prevParamCount = 0;
5595 foreach (var decl in decls) {
5597 buf.Append (NestedTypeSeparator);
5598 insertNested = true;
5599 base.AppendTypeName (buf, decl, context);
5600 int argCount = DocUtils.GetGenericArgumentCount (decl);
5601 int numArgs = argCount - prevParamCount;
5602 prevParamCount = argCount;
5604 buf.Append ('`').Append (numArgs);
5609 public override string GetDeclaration (MemberReference member)
5611 TypeReference r = member as TypeReference;
5613 return "T:" + GetTypeName (r);
5615 return base.GetDeclaration (member);
5618 protected override string GetConstructorName (MethodReference constructor)
5620 return GetMethodDefinitionName (constructor, "#ctor");
5623 protected override string GetMethodName (MethodReference method)
5626 MethodDefinition methodDef = method as MethodDefinition;
5627 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
5630 TypeReference iface;
5631 MethodReference ifaceMethod;
5632 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
5633 AddTypeCount = false;
5634 name = GetTypeName (iface) + "." + ifaceMethod.Name;
5635 AddTypeCount = true;
5637 return GetMethodDefinitionName (method, name);
5640 private string GetMethodDefinitionName (MethodReference method, string name)
5642 StringBuilder buf = new StringBuilder ();
5643 buf.Append (GetTypeName (method.DeclaringType));
5645 buf.Append (name.Replace (".", "#"));
5646 if (method.IsGenericMethod ()) {
5647 IList<GenericParameter> genArgs = method.GenericParameters;
5648 if (genArgs.Count > 0)
5649 buf.Append ("``").Append (genArgs.Count);
5651 IList<ParameterDefinition> parameters = method.Parameters;
5653 genDeclType = method.DeclaringType;
5654 genDeclMethod = method;
5655 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
5659 genDeclMethod = null;
5661 return buf.ToString ();
5664 private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
5666 if (parameters.Count == 0)
5671 AppendParameter (buf, genArgs, parameters [0]);
5672 for (int i = 1; i < parameters.Count; ++i) {
5674 AppendParameter (buf, genArgs, parameters [i]);
5677 return buf.Append (')');
5680 private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
5682 AddTypeCount = false;
5683 buf.Append (GetTypeName (parameter.ParameterType));
5684 AddTypeCount = true;
5688 protected override string GetPropertyName (PropertyReference property)
5692 PropertyDefinition propertyDef = property as PropertyDefinition;
5693 MethodDefinition method = null;
5694 if (propertyDef != null)
5695 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
5696 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
5697 name = property.Name;
5699 TypeReference iface;
5700 MethodReference ifaceMethod;
5701 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5702 AddTypeCount = false;
5703 name = string.Join ("#", new string[]{
5704 GetTypeName (iface).Replace (".", "#"),
5705 DocUtils.GetMember (property.Name)
5707 AddTypeCount = true;
5710 StringBuilder buf = new StringBuilder ();
5711 buf.Append (GetName (property.DeclaringType));
5714 IList<ParameterDefinition> parameters = property.Parameters;
5715 if (parameters.Count > 0) {
5716 genDeclType = property.DeclaringType;
5718 IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
5719 AppendParameter (buf, genArgs, parameters [0]);
5720 for (int i = 1; i < parameters.Count; ++i) {
5722 AppendParameter (buf, genArgs, parameters [i]);
5727 return buf.ToString ();
5730 protected override string GetFieldName (FieldReference field)
5732 return string.Format ("{0}.{1}",
5733 GetName (field.DeclaringType), field.Name);
5736 protected override string GetEventName (EventReference e)
5738 return string.Format ("{0}.{1}",
5739 GetName (e.DeclaringType), e.Name);
5742 protected override string GetTypeDeclaration (TypeDefinition type)
5744 string name = GetName (type);
5750 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5752 string name = GetName (constructor);
5758 protected override string GetMethodDeclaration (MethodDefinition method)
5760 string name = GetName (method);
5763 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
5764 genDeclType = method.DeclaringType;
5765 genDeclMethod = method;
5766 name += "~" + GetName (method.ReturnType);
5768 genDeclMethod = null;
5773 protected override string GetPropertyDeclaration (PropertyDefinition property)
5775 string name = GetName (property);
5781 protected override string GetFieldDeclaration (FieldDefinition field)
5783 string name = GetName (field);
5789 protected override string GetEventDeclaration (EventDefinition e)
5791 string name = GetName (e);
5798 class FileNameMemberFormatter : SlashDocMemberFormatter {
5799 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5804 protected override char NestedTypeSeparator {
5809 class ResolvedTypeInfo {
5810 TypeDefinition typeDef;
5812 public ResolvedTypeInfo (TypeReference value) {
5816 public TypeReference Reference { get; private set; }
5818 public TypeDefinition Definition {
5820 if (typeDef == null) {
5821 typeDef = Reference.Resolve ();
5828 /// <summary>Formats attribute values. Should return true if it is able to format the value.</summary>
5829 class AttributeValueFormatter {
5830 public virtual bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5832 TypeReference valueType = type.Reference;
5834 returnvalue = "null";
5837 if (valueType.FullName == "System.Type") {
5838 var vTypeRef = v as TypeReference;
5839 if (vTypeRef != null)
5840 returnvalue = "typeof(" + NativeTypeManager.GetTranslatedName (vTypeRef) + ")"; // TODO: drop NS handling
5842 returnvalue = "typeof(" + v.ToString () + ")";
5846 if (valueType.FullName == "System.String") {
5847 returnvalue = "\"" + v.ToString () + "\"";
5850 if (valueType.FullName == "System.Char") {
5851 returnvalue = "'" + v.ToString () + "'";
5855 returnvalue = (bool)v ? "true" : "false";
5859 TypeDefinition valueDef = type.Definition;
5860 if (valueDef == null || !valueDef.IsEnum) {
5861 returnvalue = v.ToString ();
5865 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5866 var values = MDocUpdater.GetEnumerationValues (valueDef);
5867 long c = MDocUpdater.ToInt64 (v);
5868 if (values.ContainsKey (c)) {
5869 returnvalue = typename + "." + values [c];
5878 /// <summary>The final value formatter in the pipeline ... if no other formatter formats the value,
5879 /// then this one will serve as the default implementation.</summary>
5880 class DefaultAttributeValueFormatter : AttributeValueFormatter {
5881 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5883 returnvalue = "(" + MDocUpdater.GetDocTypeFullName (type.Reference) + ") " + v.ToString ();
5888 /// <summary>Flags enum formatter that assumes powers of two values.</summary>
5889 /// <remarks>As described here: https://msdn.microsoft.com/en-us/library/vstudio/ms229062(v=vs.100).aspx</remarks>
5890 class StandardFlagsEnumFormatter : AttributeValueFormatter {
5891 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5893 TypeReference valueType = type.Reference;
5894 TypeDefinition valueDef = type.Definition;
5895 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
5897 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5898 var values = MDocUpdater.GetEnumerationValues (valueDef);
5899 long c = MDocUpdater.ToInt64 (v);
5900 returnvalue = string.Join (" | ",
5901 (from i in values.Keys
5902 where (c & i) == i && i != 0
5903 select typename + "." + values [i])
5904 .DefaultIfEmpty (c.ToString ()).ToArray ());
5914 /// <summary>A custom formatter for the ObjCRuntime.Platform enumeration.</summary>
5915 class ApplePlatformEnumFormatter : AttributeValueFormatter {
5916 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5918 TypeReference valueType = type.Reference;
5919 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5920 TypeDefinition valueDef = type.Definition;
5921 if (typename.Contains ("ObjCRuntime.Platform") && valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
5923 var values = MDocUpdater.GetEnumerationValues (valueDef);
5924 long c = MDocUpdater.ToInt64 (v);
5926 returnvalue = Format (c, values, typename);
5934 string Format (long c, IDictionary<long, string> values, string typename)
5936 int iosarch, iosmajor, iosminor, iossubminor;
5937 int macarch, macmajor, macminor, macsubminor;
5938 GetEncodingiOS (c, out iosarch, out iosmajor, out iosminor, out iossubminor);
5939 GetEncodingMac ((ulong)c, out macarch, out macmajor, out macminor, out macsubminor);
5941 if (iosmajor == 0 & iosminor == 0 && iossubminor == 0) {
5942 return FormatValues ("Mac", macarch, macmajor, macminor, macsubminor);
5945 if (macmajor == 0 & macminor == 0 && macsubminor == 0) {
5946 return FormatValues ("iOS", iosarch, iosmajor, iosminor, iossubminor);
5949 return string.Format ("(Platform){0}", c);
5952 string FormatValues (string plat, int arch, int major, int minor, int subminor)
5954 string archstring = "";
5963 return string.Format ("Platform.{4}_{0}_{1}{2} | Platform.{4}_Arch{3}",
5966 subminor == 0 ? "" : "_" + subminor.ToString (),
5972 void GetEncodingiOS (long entireLong, out int archindex, out int major, out int minor, out int subminor)
5974 long lowerBits = entireLong & 0xffffffff;
5975 int lowerBitsAsInt = (int) lowerBits;
5976 GetEncoding (lowerBitsAsInt, out archindex, out major, out minor, out subminor);
5979 void GetEncodingMac (ulong entireLong, out int archindex, out int major, out int minor, out int subminor)
5981 ulong higherBits = entireLong & 0xffffffff00000000;
5982 int higherBitsAsInt = (int) ((higherBits) >> 32);
5983 GetEncoding (higherBitsAsInt, out archindex, out major, out minor, out subminor);
5986 void GetEncoding (Int32 encodedBits, out int archindex, out int major, out int minor, out int subminor)
5988 // format is AAJJNNSS
5989 archindex = (int)((encodedBits & 0xFF000000) >> 24);
5990 major = (int)((encodedBits & 0x00FF0000) >> 16);
5991 minor = (int)((encodedBits & 0x0000FF00) >> 8);
5992 subminor = (int)((encodedBits & 0x000000FF) >> 0);