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();
136 string apistyle = string.Empty;
140 bool show_exceptions;
141 bool no_assembly_versions, ignore_missing_types;
142 ExceptionLocations? exceptions;
144 internal int additions = 0, deletions = 0;
146 List<DocumentationImporter> importers = new List<DocumentationImporter> ();
148 DocumentationEnumerator docEnum;
152 static readonly MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
153 static readonly MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
155 static MemberFormatter[] typeFormatters = new MemberFormatter[]{
156 new CSharpMemberFormatter (),
157 new ILMemberFormatter (),
160 static MemberFormatter[] memberFormatters = new MemberFormatter[]{
161 new CSharpFullMemberFormatter (),
162 new ILFullMemberFormatter (),
165 internal static readonly MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
167 MyXmlNodeList extensionMethods = new MyXmlNodeList ();
169 HashSet<string> forwardedTypes = new HashSet<string> ();
171 public static string droppedNamespace = string.Empty;
173 public static bool HasDroppedNamespace(TypeDefinition forType)
175 return HasDroppedNamespace(forType.Module);
178 public static bool HasDroppedNamespace(MemberReference forMember)
180 return HasDroppedNamespace(forMember.Module);
183 public static bool HasDroppedNamespace(AssemblyDefinition forAssembly)
185 return HasDroppedNamespace(forAssembly.MainModule);
188 public static bool HasDroppedNamespace(ModuleDefinition forModule)
190 return !string.IsNullOrWhiteSpace (droppedNamespace) && droppedAssemblies.Any(da => da == forModule.Name);
193 public static bool HasDroppedAnyNamespace ()
195 return !string.IsNullOrWhiteSpace (droppedNamespace);
199 static List<string> droppedAssemblies = new List<string>();
201 public string PreserveTag { get; set; }
202 public static MDocUpdater Instance { get; private set; }
203 public static bool SwitchingToMagicTypes { get; private set; }
205 public override void Run (IEnumerable<string> args)
208 show_exceptions = DebugOutput;
209 var types = new List<string> ();
210 var p = new OptionSet () {
212 "Delete removed members from the XML files.",
213 v => delete = v != null },
215 "Document potential exceptions that members can generate. {SOURCES} " +
216 "is a comma-separated list of:\n" +
217 " asm Method calls in same assembly\n" +
218 " depasm Method calls in dependent assemblies\n" +
219 " all Record all possible exceptions\n" +
220 " added Modifier; only create <exception/>s\n" +
221 " for NEW types/members\n" +
222 "If nothing is specified, then only exceptions from the member will " +
224 v => exceptions = ParseExceptionLocations (v) },
226 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
229 case "ignore-missing-types":
230 ignore_missing_types = true;
232 case "no-assembly-versions":
233 no_assembly_versions = true;
236 throw new Exception ("Unsupported flag `" + v + "'.");
239 { "fignore-missing-types",
240 "Do not report an error if a --type=TYPE type\nwas not found.",
241 v => ignore_missing_types = v != null },
242 { "fno-assembly-versions",
243 "Do not generate //AssemblyVersion elements.",
244 v => no_assembly_versions = v != null },
246 "Import documentation from {FILE}.",
247 v => AddImporter (v) },
249 "Check for assembly references in {DIRECTORY}.",
250 v => assemblyResolver.AddSearchDirectory (v) },
252 "Ignored for compatibility with update-ecma-xml.",
255 "Root {DIRECTORY} to generate/update documentation.",
258 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
259 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
260 v => assemblyResolver.AddSearchDirectory (Path.GetDirectoryName (v)) },
262 "Manually specify the assembly {VERSION} that new members were added in.",
265 "Only update documentation for {TYPE}.",
266 v => types.Add (v) },
268 "When processing assembly {ASSEMBLY}, strip off leading namespace {PREFIX}:\n" +
269 " e.g. --dropns ASSEMBLY=PREFIX",
271 var parts = v.Split ('=');
272 if (parts.Length != 2) { Console.Error.WriteLine ("Invalid dropns input"); return; }
273 var assembly = Path.GetFileName (parts [0].Trim ());
274 var prefix = parts [1].Trim();
275 droppedAssemblies.Add (assembly);
276 droppedNamespace = prefix;
279 "If the new assembly is switching to 'magic types', then this switch should be defined.",
280 v => SwitchingToMagicTypes = true },
282 "Do not delete members that don't exist in the assembly, but rather mark them as preserved.",
283 v => PreserveTag = "true" },
285 "Allow types to be in multiple assemblies.",
286 v => multiassembly = true },
288 "Denotes the apistyle. Currently, only `classic` and `unified` are supported. `classic` set of assemblies should be run first, immediately followed by 'unified' assemblies with the `dropns` parameter.",
289 v => apistyle = v.ToLowerInvariant ()},
291 var assemblies = Parse (p, args, "update",
292 "[OPTIONS]+ ASSEMBLIES",
293 "Create or update documentation from ASSEMBLIES.");
294 if (assemblies == null)
296 if (assemblies.Count == 0)
297 Error ("No assemblies specified.");
299 // validation for the api-style parameter
300 if (apistyle == "classic")
302 else if (apistyle == "unified") {
303 if (!droppedAssemblies.Any ())
304 Error ("api-style 'unified' must also supply the 'dropns' parameter with at least one assembly and dropped namespace.");
305 } else if (!string.IsNullOrWhiteSpace (apistyle))
306 Error ("api-style '{0}' is not currently supported", apistyle);
309 foreach (var dir in assemblies
310 .Where (a => a.Contains (Path.DirectorySeparatorChar))
311 .Select (a => Path.GetDirectoryName (a)))
312 assemblyResolver.AddSearchDirectory (dir);
314 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
317 throw new InvalidOperationException("The --out option is required.");
319 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
321 // Store types that have been forwarded to avoid duplicate generation
322 GatherForwardedTypes ();
324 docEnum = docEnum ?? new DocumentationEnumerator ();
326 // PERFORM THE UPDATES
328 if (types.Count > 0) {
330 DoUpdateTypes (srcPath, types, srcPath);
333 else if (opts.@namespace != null)
334 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
335 Path.Combine (dest_dir, opts.@namespace));
338 DoUpdateAssemblies (srcPath, srcPath);
340 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
342 public static bool IsInAssemblies(string name) {
343 var query = Instance.assemblies.Where (a => a.MainModule.Name == name).ToArray ();
344 return query.Length > 0;
346 void AddImporter (string path)
349 XmlReader r = new XmlTextReader (path);
351 while (r.NodeType != XmlNodeType.Element) {
353 Error ("Unable to read XML file: {0}.", path);
355 if (r.LocalName == "doc") {
356 importers.Add (new MsxdocDocumentationImporter (path));
358 else if (r.LocalName == "Libraries") {
359 var ecmadocs = new XmlTextReader (path);
360 docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
361 importers.Add (new EcmaDocumentationImporter (ecmadocs));
364 Error ("Unsupported XML format within {0}.", path);
367 } catch (Exception e) {
368 Environment.ExitCode = 1;
369 Error ("Could not load XML file: {0}.", e.Message);
373 void GatherForwardedTypes ()
375 foreach (var asm in assemblies)
376 foreach (var type in asm.MainModule.ExportedTypes.Where (t => t.IsForwarder).Select (t => t.FullName))
377 forwardedTypes.Add (type);
380 static ExceptionLocations ParseExceptionLocations (string s)
382 ExceptionLocations loc = ExceptionLocations.Member;
385 foreach (var type in s.Split (',')) {
387 case "added": loc |= ExceptionLocations.AddedMembers; break;
388 case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
389 case "asm": loc |= ExceptionLocations.Assembly; break;
390 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
391 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
397 internal void Warning (string format, params object[] args)
399 Message (TraceLevel.Warning, "mdoc: " + format, args);
402 private AssemblyDefinition LoadAssembly (string name)
404 AssemblyDefinition assembly = null;
406 assembly = AssemblyDefinition.ReadAssembly (name, new ReaderParameters { AssemblyResolver = assemblyResolver });
407 } catch (System.IO.FileNotFoundException) { }
409 if (assembly == null)
410 throw new InvalidOperationException("Assembly " + name + " not found.");
415 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
416 OrderTypeAttributes (element);
417 XmlTextWriter writer = new XmlTextWriter(output);
418 writer.Formatting = Formatting.Indented;
419 writer.Indentation = 2;
420 writer.IndentChar = ' ';
421 element.WriteTo(writer);
425 private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
427 Action<string> creator = file => {
428 using (var writer = OpenWrite (file, mode))
432 MdocFile.UpdateFile (filename, creator);
435 private static void OrderTypeAttributes (XmlElement e)
437 foreach (XmlElement type in e.SelectNodes ("//Type")) {
438 OrderTypeAttributes (type.Attributes);
442 static readonly string[] TypeAttributeOrder = {
443 "Name", "FullName", "FullNameSP", "Maintainer"
446 private static void OrderTypeAttributes (XmlAttributeCollection c)
448 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
449 for (int i = 0; i < c.Count; ++i) {
450 XmlAttribute a = c [i];
451 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
452 if (a.Name == TypeAttributeOrder [j]) {
458 for (int i = attrs.Length-1; i >= 0; --i) {
459 XmlAttribute n = attrs [i];
462 XmlAttribute r = null;
463 for (int j = i+1; j < attrs.Length; ++j) {
464 if (attrs [j] != null) {
471 if (c [n.Name] != null) {
472 c.RemoveNamedItem (n.Name);
473 c.InsertBefore (n, r);
478 private XmlDocument CreateIndexStub()
480 XmlDocument index = new XmlDocument();
482 XmlElement index_root = index.CreateElement("Overview");
483 index.AppendChild(index_root);
485 if (assemblies.Count == 0)
486 throw new Exception ("No assembly");
488 XmlElement index_assemblies = index.CreateElement("Assemblies");
489 index_root.AppendChild(index_assemblies);
491 XmlElement index_remarks = index.CreateElement("Remarks");
492 index_remarks.InnerText = "To be added.";
493 index_root.AppendChild(index_remarks);
495 XmlElement index_copyright = index.CreateElement("Copyright");
496 index_copyright.InnerText = "To be added.";
497 index_root.AppendChild(index_copyright);
499 XmlElement index_types = index.CreateElement("Types");
500 index_root.AppendChild(index_types);
505 private static void WriteNamespaceStub(string ns, string outdir) {
506 XmlDocument index = new XmlDocument();
508 XmlElement index_root = index.CreateElement("Namespace");
509 index.AppendChild(index_root);
511 index_root.SetAttribute("Name", ns);
513 XmlElement index_docs = index.CreateElement("Docs");
514 index_root.AppendChild(index_docs);
516 XmlElement index_summary = index.CreateElement("summary");
517 index_summary.InnerText = "To be added.";
518 index_docs.AppendChild(index_summary);
520 XmlElement index_remarks = index.CreateElement("remarks");
521 index_remarks.InnerText = "To be added.";
522 index_docs.AppendChild(index_remarks);
524 WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
525 writer => WriteXml (index.DocumentElement, writer));
528 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
530 var index = CreateIndexForTypes (dest);
532 var found = new HashSet<string> ();
533 foreach (AssemblyDefinition assembly in assemblies) {
534 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames)) {
535 string relpath = DoUpdateType (type, basepath, dest);
539 found.Add (type.FullName);
544 index.Add (assembly);
552 if (ignore_missing_types)
555 var notFound = from n in typenames where !found.Contains (n) select n;
557 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
560 class IndexForTypes {
566 XmlElement index_types;
567 XmlElement index_assemblies;
569 public IndexForTypes (MDocUpdater app, string indexFile, XmlDocument index)
572 this.indexFile = indexFile;
575 index_types = WriteElement (index.DocumentElement, "Types");
576 index_assemblies = WriteElement (index.DocumentElement, "Assemblies");
579 public void Add (AssemblyDefinition assembly)
581 if (index_assemblies.SelectSingleNode ("Assembly[@Name='" + assembly.Name.Name + "']") != null)
584 app.AddIndexAssembly (assembly, index_assemblies);
587 public void Add (TypeDefinition type)
589 app.AddIndexType (type, index_types);
594 SortIndexEntries (index_types);
595 WriteFile (indexFile, FileMode.Create,
596 writer => WriteXml (index.DocumentElement, writer));
600 IndexForTypes CreateIndexForTypes (string dest)
602 string indexFile = Path.Combine (dest, "index.xml");
603 if (File.Exists (indexFile))
605 return new IndexForTypes (this, indexFile, CreateIndexStub ());
608 /// <summary>Constructs the presumed path to the type's documentation file</summary>
609 /// <returns><c>true</c>, if the type file was found, <c>false</c> otherwise.</returns>
610 /// <param name="result">A typle that contains 1) the 'reltypefile', 2) the 'typefile', and 3) the file info</param>
611 bool TryFindTypeFile(string nsname, string typename, string basepath, out Tuple<string, string, FileInfo> result) {
612 string reltypefile = DocUtils.PathCombine (nsname, typename + ".xml");
613 string typefile = Path.Combine (basepath, reltypefile);
614 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
616 result = new Tuple<string, string, FileInfo> (reltypefile, typefile, file);
621 public string DoUpdateType (TypeDefinition type, string basepath, string dest)
623 if (type.Namespace == null)
624 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
626 if (!IsPublic (type))
629 // Must get the A+B form of the type name.
630 string typename = GetTypeFileName(type);
631 string nsname = DocUtils.GetNamespace (type);
633 // Find the file, if it exists
634 string[] searchLocations = new string[] {
638 if (MDocUpdater.HasDroppedNamespace (type)) {
639 // If dropping namespace, types may have moved into a couple of different places.
640 var newSearchLocations = searchLocations.Union (new string[] {
641 string.Format ("{0}.{1}", droppedNamespace, nsname),
642 nsname.Replace (droppedNamespace + ".", string.Empty),
643 MDocUpdater.droppedNamespace
646 searchLocations = newSearchLocations.ToArray ();
649 string reltypefile="", typefile="";
650 System.IO.FileInfo file = null;
652 foreach (var f in searchLocations) {
653 Tuple<string, string, FileInfo> result;
654 bool fileExists = TryFindTypeFile (f, typename, basepath, out result);
657 reltypefile = result.Item1;
658 typefile = result.Item2;
665 if (file == null || !file.Exists) {
666 // we were not able to find a file, let's use the original type informatio.
667 // so that we create the stub in the right place.
668 Tuple<string, string, FileInfo> result;
669 TryFindTypeFile (nsname, typename, basepath, out result);
671 reltypefile = result.Item1;
672 typefile = result.Item2;
676 string output = null;
679 } else if (dest == "-") {
682 output = Path.Combine (dest, reltypefile);
685 if (file != null && file.Exists) {
687 XmlDocument basefile = new XmlDocument();
689 basefile.Load(typefile);
690 } catch (Exception e) {
691 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
694 DoUpdateType2("Updating", basefile, type, output, false);
697 XmlElement td = StubType(type, output);
704 public void DoUpdateNS (string ns, string nspath, string outpath)
706 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
707 AssemblyDefinition assembly = assemblies [0];
709 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
710 XmlDocument basefile = new XmlDocument();
711 string typefile = Path.Combine(nspath, file.Name);
713 basefile.Load(typefile);
714 } catch (Exception e) {
715 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
719 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
720 TypeDefinition type = assembly.GetType(typename);
723 if (!string.IsNullOrWhiteSpace (droppedNamespace)) {
724 string nameWithNs = string.Format ("{0}.{1}", droppedNamespace, typename);
725 type = assembly.GetType (nameWithNs);
727 Warning ("Type no longer in assembly: " + typename);
734 seenTypes[type] = seenTypes;
735 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false);
738 // Stub types not in the directory
739 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
740 if (type.Namespace != ns || seenTypes.ContainsKey(type))
743 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"));
744 if (td == null) continue;
748 private static string GetTypeFileName (TypeReference type)
750 return filenameFormatter.GetName (type);
753 public static string GetTypeFileName (string typename)
755 StringBuilder filename = new StringBuilder (typename.Length);
759 for (int i = 0; i < typename.Length; ++i) {
760 char c = typename [i];
769 filename.Append ('`').Append ((numArgs+1).ToString());
784 return filename.ToString ();
787 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
789 XmlElement index_assembly = null;
791 index_assembly = (XmlElement)parent.SelectSingleNode ("Assembly[@Name='"+ assembly.Name.Name +"']");
793 if (index_assembly == null)
794 index_assembly = parent.OwnerDocument.CreateElement ("Assembly");
796 index_assembly.SetAttribute ("Name", assembly.Name.Name);
797 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
799 AssemblyNameDefinition name = assembly.Name;
800 if (name.HasPublicKey) {
801 XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
802 var key = new StringBuilder (name.PublicKey.Length*3 + 2);
804 foreach (byte b in name.PublicKey)
805 key.AppendFormat ("{0,2:x2} ", b);
807 pubkey.InnerText = key.ToString ();
808 index_assembly.AppendChild (pubkey);
811 if (!string.IsNullOrEmpty (name.Culture)) {
812 XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
813 culture.InnerText = name.Culture;
814 index_assembly.AppendChild (culture);
817 MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
818 parent.AppendChild(index_assembly);
821 private void AddIndexType (TypeDefinition type, XmlElement index_types)
823 string typename = GetTypeFileName(type);
825 // Add namespace and type nodes into the index file as needed
826 string ns = DocUtils.GetNamespace (type);
827 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode ("Namespace[@Name='" + ns + "']");
828 if (nsnode == null) {
829 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
830 nsnode.SetAttribute ("Name", ns);
831 index_types.AppendChild (nsnode);
833 string doc_typename = GetDocTypeName (type);
834 XmlElement typenode = (XmlElement) nsnode.SelectSingleNode ("Type[@Name='" + typename + "']");
835 if (typenode == null) {
836 typenode = index_types.OwnerDocument.CreateElement ("Type");
837 typenode.SetAttribute ("Name", typename);
838 nsnode.AppendChild (typenode);
840 if (typename != doc_typename)
841 typenode.SetAttribute("DisplayName", doc_typename);
843 typenode.RemoveAttribute("DisplayName");
845 typenode.SetAttribute ("Kind", GetTypeKind (type));
848 private void DoUpdateAssemblies (string source, string dest)
850 string indexfile = dest + "/index.xml";
852 if (System.IO.File.Exists(indexfile)) {
853 index = new XmlDocument();
854 index.Load(indexfile);
857 ClearElement(index.DocumentElement, "Assembly");
858 ClearElement(index.DocumentElement, "Attributes");
860 index = CreateIndexStub();
863 string defaultTitle = "Untitled";
864 if (assemblies.Count == 1)
865 defaultTitle = assemblies[0].Name.Name;
866 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
868 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
869 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
871 index_assemblies.RemoveAll ();
874 HashSet<string> goodfiles = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
876 foreach (AssemblyDefinition assm in assemblies) {
877 AddIndexAssembly (assm, index_assemblies);
878 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
881 SortIndexEntries (index_types);
883 CleanupFiles (dest, goodfiles);
884 CleanupIndexTypes (index_types, goodfiles);
885 CleanupExtensions (index_types);
887 WriteFile (indexfile, FileMode.Create,
888 writer => WriteXml(index.DocumentElement, writer));
891 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
893 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
895 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
896 string typename = GetTypeFileName(type);
897 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0 || forwardedTypes.Contains (type.FullName))
900 string reltypepath = DoUpdateType (type, source, dest);
901 if (reltypepath == null)
904 // Add namespace and type nodes into the index file as needed
905 AddIndexType (type, index_types);
907 // Ensure the namespace index file exists
908 string namespaceToUse = type.Namespace;
909 if (HasDroppedNamespace(assembly)) {
910 namespaceToUse = string.Format ("{0}.{1}", droppedNamespace, namespaceToUse);
912 string onsdoc = DocUtils.PathCombine (dest, namespaceToUse + ".xml");
913 string nsdoc = DocUtils.PathCombine (dest, "ns-" + namespaceToUse + ".xml");
914 if (File.Exists (onsdoc)) {
915 File.Move (onsdoc, nsdoc);
918 if (!File.Exists (nsdoc)) {
919 Console.WriteLine("New Namespace File: " + type.Namespace);
920 WriteNamespaceStub(namespaceToUse, dest);
923 goodfiles.Add (reltypepath);
927 private static void SortIndexEntries (XmlElement indexTypes)
929 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
930 XmlNodeComparer c = new AttributeNameComparer ();
931 SortXmlNodes (indexTypes, namespaces, c);
933 for (int i = 0; i < namespaces.Count; ++i)
934 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
937 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
939 MyXmlNodeList l = new MyXmlNodeList (children.Count);
940 for (int i = 0; i < children.Count; ++i)
941 l.Add (children [i]);
943 for (int i = l.Count - 1; i > 0; --i) {
944 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
948 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
950 public abstract int Compare (XmlNode x, XmlNode y);
952 public int Compare (object x, object y)
954 return Compare ((XmlNode) x, (XmlNode) y);
958 class AttributeNameComparer : XmlNodeComparer {
961 public AttributeNameComparer ()
966 public AttributeNameComparer (string attribute)
968 this.attribute = attribute;
971 public override int Compare (XmlNode x, XmlNode y)
973 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
977 class VersionComparer : XmlNodeComparer {
978 public override int Compare (XmlNode x, XmlNode y)
980 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
981 string a = GetVersion (x.InnerText);
982 string b = GetVersion (y.InnerText);
983 return new Version (a).CompareTo (new Version (b));
986 static string GetVersion (string v)
988 int n = v.IndexOf ("x");
991 return v.Substring (0, n-1);
995 private static string GetTypeKind (TypeDefinition type)
998 return "Enumeration";
999 if (type.IsValueType)
1001 if (type.IsInterface)
1003 if (DocUtils.IsDelegate (type))
1005 if (type.IsClass || type.FullName == "System.Enum") // FIXME
1007 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
1010 public static bool IsPublic (TypeDefinition type)
1012 TypeDefinition decl = type;
1013 while (decl != null) {
1014 if (!(decl.IsPublic || decl.IsNestedPublic ||
1015 decl.IsNestedFamily || decl.IsNestedFamily || decl.IsNestedFamilyOrAssembly)) {
1018 decl = (TypeDefinition) decl.DeclaringType;
1023 private void CleanupFiles (string dest, HashSet<string> goodfiles)
1025 // Look for files that no longer correspond to types
1026 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
1027 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
1028 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
1029 if (!goodfiles.Contains (relTypeFile)) {
1030 XmlDocument doc = new XmlDocument ();
1031 doc.Load (typefile.FullName);
1032 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
1033 var assemblyNameNode = doc.SelectSingleNode ("/Type/AssemblyInfo/AssemblyName");
1034 if (assemblyNameNode == null){
1035 Warning ("Did not find /Type/AssemblyInfo/AssemblyName on {0}", typefile.FullName);
1038 string assemblyName = assemblyNameNode.InnerText;
1039 AssemblyDefinition assembly = assemblies.FirstOrDefault (a => a.Name.Name == assemblyName);
1041 Action saveDoc = () => {
1042 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
1043 WriteXml(doc.DocumentElement, writer);
1046 if (e != null && !no_assembly_versions && assembly != null && assemblyName != null && UpdateAssemblyVersions (e, assembly, GetAssemblyVersions(assemblyName), false)) {
1048 goodfiles.Add (relTypeFile);
1052 Action actuallyDelete = () => {
1053 string newname = typefile.FullName + ".remove";
1054 try { System.IO.File.Delete (newname); } catch (Exception) { Warning ("Unable to delete existing file: {0}", newname); }
1055 try { typefile.MoveTo (newname); } catch (Exception) { Warning ("Unable to rename to: {0}", newname); }
1056 Console.WriteLine ("Class no longer present; file renamed: " + Path.Combine (nsdir.Name, typefile.Name));
1059 if (string.IsNullOrWhiteSpace (PreserveTag)) { // only do this if there was not a -preserve
1062 var unifiedAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[@apistyle='unified']");
1063 var classicAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[not(@apistyle) or @apistyle='classic']");
1064 var unifiedMembers = doc.SelectNodes ("//Member[@apistyle='unified']|//Member/AssemblyInfo[@apistyle='unified']");
1065 var classicMembers = doc.SelectNodes ("//Member[@apistyle='classic']|//Member/AssemblyInfo[@apistyle='classic']");
1066 bool isUnifiedRun = HasDroppedAnyNamespace ();
1067 bool isClassicOrNormalRun = !isUnifiedRun;
1069 Action<XmlNode, ApiStyle> removeStyles = (x, style) => {
1070 var styledNodes = doc.SelectNodes("//*[@apistyle='"+ style.ToString ().ToLowerInvariant () +"']");
1071 if (styledNodes != null && styledNodes.Count > 0) {
1072 foreach(var node in styledNodes.Cast<XmlNode> ()) {
1073 node.ParentNode.RemoveChild (node);
1078 if (isClassicOrNormalRun) {
1079 if (unifiedAssemblyNode != null || unifiedMembers.Count > 0) {
1080 Warning ("*** this type is marked as unified, not deleting during this run: {0}", typefile.FullName);
1081 // if truly removed from both assemblies, it will be removed fully during the unified run
1082 removeStyles (doc, ApiStyle.Classic);
1085 // we should be safe to delete here because it was not marked as a unified assembly
1090 if (classicAssemblyNode != null || classicMembers.Count > 0) {
1091 Warning ("*** this type is marked as classic, not deleting {0}", typefile.FullName);
1094 // safe to delete because it wasn't marked as a classic assembly, so the type is gone in both.
1104 private static TextWriter OpenWrite (string path, FileMode mode)
1106 var w = new StreamWriter (
1107 new FileStream (path, mode),
1108 new UTF8Encoding (false)
1114 private string[] GetAssemblyVersions (string assemblyName)
1116 return (from a in assemblies
1117 where a.Name.Name == assemblyName
1118 select GetAssemblyVersion (a)).ToArray ();
1121 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
1123 // Look for type nodes that no longer correspond to types
1124 MyXmlNodeList remove = new MyXmlNodeList ();
1125 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
1126 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
1127 if (!goodfiles.Contains (fulltypename)) {
1128 remove.Add (typenode);
1131 foreach (XmlNode n in remove)
1132 n.ParentNode.RemoveChild (n);
1135 private void CleanupExtensions (XmlElement index_types)
1137 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
1138 if (extensionMethods.Count == 0) {
1141 index_types.SelectSingleNode ("/Overview").RemoveChild (e);
1145 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
1146 index_types.SelectSingleNode ("/Overview").AppendChild (e);
1150 extensionMethods.Sort (DefaultExtensionMethodComparer);
1151 foreach (XmlNode m in extensionMethods) {
1152 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
1156 class ExtensionMethodComparer : XmlNodeComparer {
1157 public override int Compare (XmlNode x, XmlNode y)
1159 XmlNode xLink = x.SelectSingleNode ("Member/Link");
1160 XmlNode yLink = y.SelectSingleNode ("Member/Link");
1162 int n = xLink.Attributes ["Type"].Value.CompareTo (
1163 yLink.Attributes ["Type"].Value);
1166 n = xLink.Attributes ["Member"].Value.CompareTo (
1167 yLink.Attributes ["Member"].Value);
1168 if (n == 0 && !object.ReferenceEquals (x, y))
1169 throw new InvalidOperationException ("Duplicate extension method found!");
1174 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
1176 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince)
1178 Console.WriteLine(message + ": " + type.FullName);
1180 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
1182 // Update type metadata
1183 UpdateType(basefile.DocumentElement, type);
1185 // Update existing members. Delete member nodes that no longer should be there,
1186 // and remember what members are already documented so we don't add them again.
1188 MyXmlNodeList todelete = new MyXmlNodeList ();
1190 foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
1191 XmlElement oldmember = info.Node;
1192 MemberReference oldmember2 = info.Member;
1194 if (info.Member != null && info.Node != null) {
1195 // Check for an error condition where the xml MemberName doesn't match the matched member
1196 var memberName = GetMemberName (info.Member);
1197 var memberAttribute = info.Node.Attributes ["MemberName"];
1198 if (memberAttribute == null || (memberAttribute.Value != memberName && memberAttribute.Value.Split (',').Length != memberName.Split (',').Length)) {
1199 oldmember.SetAttribute ("MemberName", memberName);
1203 string sig = oldmember2 != null ? memberFormatters [0].GetDeclaration (oldmember2) : null;
1205 // Interface implementations and overrides are deleted from the docs
1206 // unless the overrides option is given.
1207 if (oldmember2 != null && sig == null)
1210 // Deleted (or signature changed)
1211 if (oldmember2 == null) {
1212 if (!no_assembly_versions && UpdateAssemblyVersions (oldmember, type.Module.Assembly, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
1215 DeleteMember ("Member Removed", output, oldmember, todelete, type);
1220 if (seenmembers.ContainsKey (sig)) {
1221 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
1222 // ignore, already seen
1224 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
1225 DeleteMember ("Duplicate Member Found", output, oldmember, todelete, type);
1227 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
1231 // Update signature information
1234 // get all apistyles of sig from info.Node
1235 var styles = oldmember.GetElementsByTagName ("MemberSignature").Cast<XmlElement> ()
1236 .Where (x => x.GetAttribute ("Language") == "C#" && !seenmembers.ContainsKey(x.GetAttribute("Value")))
1237 .Select (x => x.GetAttribute ("Value"));
1239 foreach (var stylesig in styles) {
1240 seenmembers.Add (stylesig, oldmember);
1243 foreach (XmlElement oldmember in todelete)
1244 oldmember.ParentNode.RemoveChild (oldmember);
1247 if (!DocUtils.IsDelegate (type)) {
1248 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
1249 var typemembers = type.GetMembers()
1251 if (m is TypeDefinition) return false;
1252 string sig = memberFormatters [0].GetDeclaration (m);
1253 if (sig == null) return false;
1254 if (seenmembers.ContainsKey(sig)) return false;
1256 // Verify that the member isn't an explicitly implemented
1257 // member of an internal interface, in which case we shouldn't return true.
1258 MethodDefinition methdef = null;
1259 if (m is MethodDefinition)
1260 methdef = m as MethodDefinition;
1261 else if (m is PropertyDefinition) {
1262 var prop = m as PropertyDefinition;
1263 methdef = prop.GetMethod ?? prop.SetMethod;
1266 if (methdef != null) {
1267 TypeReference iface;
1268 MethodReference imethod;
1270 if (methdef.Overrides.Count == 1) {
1271 DocUtils.GetInfoForExplicitlyImplementedMethod (methdef, out iface, out imethod);
1272 if (!IsPublic (iface.Resolve ())) return false;
1279 foreach (MemberReference m in typemembers) {
1280 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
1281 if (mm == null) continue;
1283 if (MDocUpdater.SwitchingToMagicTypes || MDocUpdater.HasDroppedNamespace (m)) {
1284 // this is a unified style API that obviously doesn't exist in the classic API. Let's mark
1285 // it with apistyle="unified", so that it's not displayed for classic style APIs
1286 mm.AddApiStyle (ApiStyle.Unified);
1289 members.AppendChild( mm );
1291 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
1296 // Import code snippets from files
1297 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
1298 if (!(code is XmlElement)) continue;
1299 string file = ((XmlElement)code).GetAttribute("src");
1300 string lang = ((XmlElement)code).GetAttribute("lang");
1302 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
1304 code.InnerText = src;
1308 if (insertSince && since != null) {
1309 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
1310 docs.AppendChild (CreateSinceNode (basefile));
1314 XmlElement d = basefile.DocumentElement ["Docs"];
1315 XmlElement m = basefile.DocumentElement ["Members"];
1316 if (d != null && m != null)
1317 basefile.DocumentElement.InsertBefore (
1318 basefile.DocumentElement.RemoveChild (d), m);
1319 SortTypeMembers (m);
1323 WriteXml(basefile.DocumentElement, Console.Out);
1325 FileInfo file = new FileInfo (output);
1326 if (!file.Directory.Exists) {
1327 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
1328 file.Directory.Create ();
1330 WriteFile (output, FileMode.Create,
1331 writer => WriteXml(basefile.DocumentElement, writer));
1335 private string GetCodeSource (string lang, string file)
1338 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
1339 // Grab the specified region
1340 string region = "#region " + file.Substring (anchorStart + 4);
1341 file = file.Substring (0, anchorStart + 3);
1343 using (StreamReader reader = new StreamReader (file)) {
1345 StringBuilder src = new StringBuilder ();
1347 while ((line = reader.ReadLine ()) != null) {
1348 if (line.Trim() == region) {
1349 indent = line.IndexOf (region);
1352 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
1357 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
1360 return src.ToString ();
1362 } catch (Exception e) {
1363 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
1364 file, region, show_exceptions ? e.ToString () : e.Message);
1369 using (StreamReader reader = new StreamReader (file))
1370 return reader.ReadToEnd ();
1371 } catch (Exception e) {
1372 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
1377 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete, TypeDefinition type)
1379 string format = output != null
1380 ? "{0}: File='{1}'; Signature='{4}'"
1381 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1382 string signature = member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value;
1386 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1387 member.Attributes ["MemberName"].Value,
1390 // Identify all of the different states that could affect our decision to delete the member
1391 bool shouldPreserve = !string.IsNullOrWhiteSpace (PreserveTag);
1392 bool hasContent = MemberDocsHaveUserContent (member);
1393 bool shouldDelete = !shouldPreserve && (delete || !hasContent);
1395 bool unifiedRun = HasDroppedNamespace (type);
1397 var classicAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[not(@apistyle) or @apistyle='classic']");
1398 bool nodeIsClassic = classicAssemblyInfo != null || member.HasApiStyle (ApiStyle.Classic);
1399 var unifiedAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']");
1400 bool nodeIsUnified = unifiedAssemblyInfo != null || member.HasApiStyle (ApiStyle.Unified);
1402 Action actuallyDelete = () => {
1403 todelete.Add (member);
1407 if (!shouldDelete) {
1408 // explicitly not deleting
1409 string message = shouldPreserve ?
1410 "Not deleting '{0}' due to --preserve." :
1411 "Not deleting '{0}'; must be enabled with the --delete option";
1412 Warning (message, signature);
1413 } else if (unifiedRun && nodeIsClassic) {
1414 // this is a unified run, and the member doesn't exist, but is marked as being in the classic assembly.
1415 member.RemoveApiStyle (ApiStyle.Unified);
1416 member.AddApiStyle (ApiStyle.Classic);
1417 Warning ("Not removing '{0}' since it's still in the classic assembly.", signature);
1418 } else if (unifiedRun && !nodeIsClassic) {
1419 // unified run, and the node is not classic, which means it doesn't exist anywhere.
1422 if (!isClassicRun || (isClassicRun && !nodeIsClassic && !nodeIsUnified)) { // regular codepath (ie. not classic/unified)
1424 } else { // this is a classic run
1425 Warning ("Removing classic from '{0}' ... will be removed in the unified run if not present there.", signature);
1426 member.RemoveApiStyle (ApiStyle.Classic);
1427 if (classicAssemblyInfo != null) {
1428 member.RemoveChild (classicAssemblyInfo);
1434 class MemberComparer : XmlNodeComparer {
1435 public override int Compare (XmlNode x, XmlNode y)
1438 string xMemberName = x.Attributes ["MemberName"].Value;
1439 string yMemberName = y.Attributes ["MemberName"].Value;
1441 // generic methods *end* with '>'
1442 // it's possible for explicitly implemented generic interfaces to
1443 // contain <...> without being a generic method
1444 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1445 (r = xMemberName.CompareTo (yMemberName)) != 0)
1449 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1450 xMemberName = xMemberName.Substring (0, lt);
1451 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1452 yMemberName = yMemberName.Substring (0, lt);
1453 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1456 // if @MemberName matches, then it's either two different types of
1457 // members sharing the same name, e.g. field & property, or it's an
1458 // overloaded method.
1459 // for different type, sort based on MemberType value.
1460 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1461 y.SelectSingleNode ("MemberType").InnerText);
1465 // same type -- must be an overloaded method. Sort based on type
1466 // parameter count, then parameter count, then by the parameter
1468 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1469 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1470 if (xTypeParams.Count != yTypeParams.Count)
1471 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1472 for (int i = 0; i < xTypeParams.Count; ++i) {
1473 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1474 yTypeParams [i].Attributes ["Name"].Value);
1479 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1480 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1481 if (xParams.Count != yParams.Count)
1482 return xParams.Count <= yParams.Count ? -1 : 1;
1483 for (int i = 0; i < xParams.Count; ++i) {
1484 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1485 yParams [i].Attributes ["Type"].Value);
1489 // all parameters match, but return value might not match if it was
1490 // changed between one version and another.
1491 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1492 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1493 if (xReturn != null && yReturn != null) {
1494 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1503 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1505 private static void SortTypeMembers (XmlNode members)
1507 if (members == null)
1509 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1512 private static bool MemberDocsHaveUserContent (XmlNode e)
1514 e = (XmlElement)e.SelectSingleNode("Docs");
1515 if (e == null) return false;
1516 foreach (XmlElement d in e.SelectNodes("*"))
1517 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1522 // UPDATE HELPER FUNCTIONS
1524 // CREATE A STUB DOCUMENTATION FILE
1526 public XmlElement StubType (TypeDefinition type, string output)
1528 string typesig = typeFormatters [0].GetDeclaration (type);
1529 if (typesig == null) return null; // not publicly visible
1531 XmlDocument doc = new XmlDocument();
1532 XmlElement root = doc.CreateElement("Type");
1533 doc.AppendChild (root);
1535 DoUpdateType2 ("New Type", doc, type, output, true);
1540 private XmlElement CreateSinceNode (XmlDocument doc)
1542 XmlElement s = doc.CreateElement ("since");
1543 s.SetAttribute ("version", since);
1547 // STUBBING/UPDATING FUNCTIONS
1549 public void UpdateType (XmlElement root, TypeDefinition type)
1551 root.SetAttribute("Name", GetDocTypeName (type));
1552 root.SetAttribute("FullName", GetDocTypeFullName (type));
1554 foreach (MemberFormatter f in typeFormatters) {
1555 string element = "TypeSignature[@Language='" + f.Language + "']";
1556 string valueToUse = f.GetDeclaration (type);
1559 root.SelectNodes (element).Cast<XmlElement> ().ToArray (),
1560 x => x.GetAttribute ("Value") == valueToUse,
1561 x => x.SetAttribute ("Value", valueToUse),
1563 var node = WriteElementAttribute (root, element, "Language", f.Language, forceNewElement: true);
1564 var newnode = WriteElementAttribute (root, node, "Value", valueToUse);
1570 AddAssemblyNameToNode (root, type);
1572 string assemblyInfoNodeFilter = MDocUpdater.HasDroppedNamespace (type) ? "[@apistyle='unified']" : "[not(@apistyle) or @apistyle='classic']";
1573 Func<XmlElement, bool> assemblyFilter = x => x.SelectSingleNode ("AssemblyName").InnerText == type.Module.Assembly.Name.Name;
1574 foreach(var ass in root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ().Where (assemblyFilter))
1576 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1577 if (!no_assembly_versions) {
1578 UpdateAssemblyVersions (ass, type, true);
1581 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1582 foreach (var version in versions)
1583 ass.RemoveChild (version);
1585 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1586 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1588 ClearElement(ass, "AssemblyCulture");
1591 // Why-oh-why do we put assembly attributes in each type file?
1592 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1593 // since they're outdated in current docs, and a waste of space.
1594 //MakeAttributes(ass, type.Assembly, true);
1595 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1596 if (assattrs != null)
1597 ass.RemoveChild(assattrs);
1599 NormalizeWhitespace(ass);
1602 if (type.IsGenericType ()) {
1603 MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
1605 ClearElement(root, "TypeParameters");
1608 if (type.BaseType != null) {
1609 XmlElement basenode = WriteElement(root, "Base");
1611 string basetypename = GetDocTypeFullName (type.BaseType);
1612 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1613 WriteElementText(root, "Base/BaseTypeName", basetypename);
1615 // Document how this type instantiates the generic parameters of its base type
1616 TypeReference origBase = type.BaseType.GetElementType ();
1617 if (origBase.IsGenericType ()) {
1618 ClearElement(basenode, "BaseTypeArguments");
1619 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1620 IList<TypeReference> baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1621 IList<GenericParameter> baseGenParams = origBase.GenericParameters;
1622 if (baseGenArgs.Count != baseGenParams.Count)
1623 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1624 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1625 GenericParameter param = baseGenParams [i];
1626 TypeReference value = baseGenArgs [i];
1628 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1629 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1630 bta.AppendChild(arg);
1631 arg.SetAttribute ("TypeParamName", param.Name);
1632 arg.InnerText = GetDocTypeFullName (value);
1636 ClearElement(root, "Base");
1639 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1640 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1641 List<string> interface_names = userInterfaces
1642 .Select (iface => GetDocTypeFullName (iface))
1646 XmlElement interfaces = WriteElement(root, "Interfaces");
1647 interfaces.RemoveAll();
1648 foreach (string iname in interface_names) {
1649 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1650 interfaces.AppendChild(iface);
1651 WriteElementText(iface, "InterfaceName", iname);
1654 ClearElement(root, "Interfaces");
1657 MakeAttributes (root, GetCustomAttributes (type), type);
1659 if (DocUtils.IsDelegate (type)) {
1660 MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
1661 var member = type.GetMethod ("Invoke");
1662 MakeParameters(root, member, member.Parameters);
1663 MakeReturnValue(root, member);
1666 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1667 MakeDocNode (typeInfo);
1669 if (!DocUtils.IsDelegate (type))
1670 WriteElement (root, "Members");
1672 OrderTypeNodes (root, root.ChildNodes);
1673 NormalizeWhitespace(root);
1676 /// <summary>Adds an AssemblyInfo with AssemblyName node to an XmlElement.</summary>
1677 /// <returns>The assembly that was either added, or was already present</returns>
1678 XmlElement AddAssemblyNameToNode (XmlElement root, TypeDefinition type)
1680 return AddAssemblyNameToNode (root, type.Module);
1683 /// <summary>Adds an AssemblyInfo with AssemblyName node to an XmlElement.</summary>
1684 /// <returns>The assembly that was either added, or was already present</returns>
1685 XmlElement AddAssemblyNameToNode (XmlElement root, ModuleDefinition module)
1687 Func<XmlElement, bool> assemblyFilter = x => {
1688 var existingName = x.SelectSingleNode ("AssemblyName");
1690 bool apiStyleMatches = true;
1691 string currentApiStyle = x.GetAttribute ("apistyle");
1692 if ((HasDroppedNamespace (module) && !string.IsNullOrWhiteSpace (currentApiStyle) && currentApiStyle != "unified") ||
1693 (isClassicRun && (string.IsNullOrWhiteSpace (currentApiStyle) || currentApiStyle != "classic"))) {
1694 apiStyleMatches = false;
1696 return apiStyleMatches && (existingName == null || (existingName != null && existingName.InnerText == module.Assembly.Name.Name));
1699 return AddAssemblyXmlNode (
1700 root.SelectNodes ("AssemblyInfo").Cast<XmlElement> ().ToArray (),
1701 assemblyFilter, x => WriteElementText (x, "AssemblyName", module.Assembly.Name.Name),
1703 XmlElement ass = WriteElement (root, "AssemblyInfo", forceNewElement: true);
1705 if (MDocUpdater.HasDroppedNamespace (module))
1706 ass.AddApiStyle (ApiStyle.Unified);
1708 ass.AddApiStyle (ApiStyle.Classic);
1713 static readonly string[] TypeNodeOrder = {
1717 "ThreadingSafetyStatement",
1718 "ThreadSafetyStatement",
1730 static void OrderTypeNodes (XmlNode member, XmlNodeList children)
1732 ReorderNodes (member, children, TypeNodeOrder);
1735 internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1737 List<T> l = new List<T> (list);
1742 private void UpdateMember (DocsNodeInfo info)
1744 XmlElement me = (XmlElement) info.Node;
1745 MemberReference mi = info.Member;
1747 foreach (MemberFormatter f in memberFormatters) {
1748 string element = "MemberSignature[@Language='" + f.Language + "']";
1750 var valueToUse = f.GetDeclaration (mi);
1753 me.SelectNodes (element).Cast<XmlElement> ().ToArray(),
1754 x => x.GetAttribute("Value") == valueToUse,
1755 x => x.SetAttribute ("Value", valueToUse),
1757 var node = WriteElementAttribute (me, element, "Language", f.Language, forceNewElement:true);
1758 var newNode = WriteElementAttribute (me, node, "Value", valueToUse);
1765 WriteElementText(me, "MemberType", GetMemberType(mi));
1767 if (!no_assembly_versions) {
1769 UpdateAssemblyVersions (me, mi, true);
1771 var node = AddAssemblyNameToNode (me, mi.Module);
1773 UpdateAssemblyVersionForAssemblyInfo (node, me, new[] { GetAssemblyVersion (mi.Module.Assembly) }, add: true);
1777 ClearElement (me, "AssemblyInfo");
1780 MakeAttributes (me, GetCustomAttributes (mi), mi.DeclaringType);
1782 MakeReturnValue(me, mi, MDocUpdater.HasDroppedNamespace(mi));
1783 if (mi is MethodReference) {
1784 MethodReference mb = (MethodReference) mi;
1785 if (mb.IsGenericMethod ())
1786 MakeTypeParameters (me, mb.GenericParameters, mi, MDocUpdater.HasDroppedNamespace(mi));
1788 MakeParameters(me, mi, MDocUpdater.HasDroppedNamespace(mi));
1791 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1792 WriteElementText(me, "MemberValue", fieldValue);
1794 info.Node = WriteElement (me, "Docs");
1796 OrderMemberNodes (me, me.ChildNodes);
1797 UpdateExtensionMethods (me, info);
1800 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, MemberReference member) {
1801 AddXmlNode (relevant, valueMatches, setValue, makeNewNode, member.Module);
1804 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, TypeDefinition type) {
1805 AddXmlNode (relevant, valueMatches, setValue, makeNewNode, type.Module);
1808 static XmlElement AddAssemblyXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, ModuleDefinition module)
1810 bool isUnified = MDocUpdater.HasDroppedNamespace (module);
1811 XmlElement thisAssemblyNode = relevant.FirstOrDefault (valueMatches);
1812 if (thisAssemblyNode == null) {
1813 thisAssemblyNode = makeNewNode ();
1815 setValue (thisAssemblyNode);
1818 thisAssemblyNode.AddApiStyle (ApiStyle.Unified);
1820 foreach (var otherNodes in relevant.Where (n => n != thisAssemblyNode && n.DoesNotHaveApiStyle (ApiStyle.Unified))) {
1821 otherNodes.AddApiStyle (ApiStyle.Classic);
1824 return thisAssemblyNode;
1827 /// <summary>Adds an xml node, reusing the node if it's available</summary>
1828 /// <param name="relevant">The existing set of nodes</param>
1829 /// <param name="valueMatches">Checks to see if the node's value matches what you're trying to write.</param>
1830 /// <param name="setValue">Sets the node's value</param>
1831 /// <param name="makeNewNode">Creates a new node, if valueMatches returns false.</param>
1832 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, ModuleDefinition module)
1834 bool shouldDuplicate = MDocUpdater.HasDroppedNamespace (module);
1835 var styleToUse = shouldDuplicate ? ApiStyle.Unified : ApiStyle.Classic;
1836 var existing = relevant;
1838 bool addedOldApiStyle = false;
1840 if (shouldDuplicate) {
1841 existing = existing.Where (n => n.HasApiStyle (styleToUse)).ToArray ();
1842 foreach (var n in relevant.Where (n => n.DoesNotHaveApiStyle (styleToUse))) {
1843 if (valueMatches (n)) {
1847 n.AddApiStyle (ApiStyle.Classic);
1848 addedOldApiStyle = true;
1853 if (!existing.Any ()) {
1854 var newNode = makeNewNode ();
1855 if (shouldDuplicate && addedOldApiStyle) {
1856 newNode.AddApiStyle (ApiStyle.Unified);
1860 var itemToReuse = existing.First ();
1861 setValue (itemToReuse);
1863 if (shouldDuplicate && addedOldApiStyle) {
1864 itemToReuse.AddApiStyle (styleToUse);
1870 static readonly string[] MemberNodeOrder = {
1885 static void OrderMemberNodes (XmlNode member, XmlNodeList children)
1887 ReorderNodes (member, children, MemberNodeOrder);
1890 static void ReorderNodes (XmlNode node, XmlNodeList children, string[] ordering)
1892 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1893 for (int i = 0; i < ordering.Length; ++i) {
1894 for (int j = 0; j < children.Count; ++j) {
1895 XmlNode c = children [j];
1896 if (c.Name == ordering [i]) {
1897 newChildren.Add (c);
1901 if (newChildren.Count >= 0)
1902 node.PrependChild ((XmlNode) newChildren [0]);
1903 for (int i = 1; i < newChildren.Count; ++i) {
1904 XmlNode prev = (XmlNode) newChildren [i-1];
1905 XmlNode cur = (XmlNode) newChildren [i];
1906 node.RemoveChild (cur);
1907 node.InsertAfter (cur, prev);
1911 IEnumerable<string> GetCustomAttributes (MemberReference mi)
1913 IEnumerable<string> attrs = Enumerable.Empty<string>();
1915 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1917 attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1919 PropertyDefinition pd = mi as PropertyDefinition;
1921 if (pd.GetMethod != null)
1922 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1923 if (pd.SetMethod != null)
1924 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1927 EventDefinition ed = mi as EventDefinition;
1929 if (ed.AddMethod != null)
1930 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1931 if (ed.RemoveMethod != null)
1932 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1938 IEnumerable<string> GetCustomAttributes (IList<CustomAttribute> attributes, string prefix)
1940 foreach (CustomAttribute attribute in attributes.OrderBy (ca => ca.AttributeType.FullName)) {
1942 TypeDefinition attrType = attribute.AttributeType as TypeDefinition;
1943 if (attrType != null && !IsPublic (attrType))
1945 if (slashdocFormatter.GetName (attribute.AttributeType) == null)
1948 if (Array.IndexOf (IgnorableAttributes, attribute.AttributeType.FullName) >= 0)
1951 StringList fields = new StringList ();
1953 for (int i = 0; i < attribute.ConstructorArguments.Count; ++i) {
1954 CustomAttributeArgument argument = attribute.ConstructorArguments [i];
1955 fields.Add (MakeAttributesValueString (
1960 (from namedArg in attribute.Fields
1961 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value })
1963 (from namedArg in attribute.Properties
1964 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value }))
1965 .OrderBy (v => v.Name);
1966 foreach (var d in namedArgs)
1967 fields.Add (string.Format ("{0}={1}", d.Name,
1968 MakeAttributesValueString (d.Value, d.Type)));
1970 string a2 = String.Join(", ", fields.ToArray ());
1971 if (a2 != "") a2 = "(" + a2 + ")";
1973 string name = attribute.GetDeclaringType();
1974 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1975 yield return prefix + name + a2;
1979 static readonly string[] ValidExtensionMembers = {
1988 static readonly string[] ValidExtensionDocMembers = {
1994 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1996 MethodDefinition me = info.Member as MethodDefinition;
1999 if (info.Parameters.Count < 1)
2001 if (!DocUtils.IsExtensionMethod (me))
2004 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
2005 XmlNode member = e.CloneNode (true);
2006 em.AppendChild (member);
2007 RemoveExcept (member, ValidExtensionMembers);
2008 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
2009 WriteElementText (member, "MemberType", "ExtensionMethod");
2010 XmlElement link = member.OwnerDocument.CreateElement ("Link");
2011 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
2012 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
2013 member.AppendChild (link);
2014 AddTargets (em, info);
2016 extensionMethods.Add (em);
2019 private static void RemoveExcept (XmlNode node, string[] except)
2023 MyXmlNodeList remove = null;
2024 foreach (XmlNode n in node.ChildNodes) {
2025 if (Array.BinarySearch (except, n.Name) < 0) {
2027 remove = new MyXmlNodeList ();
2032 foreach (XmlNode n in remove)
2033 node.RemoveChild (n);
2036 private static void AddTargets (XmlNode member, DocsNodeInfo info)
2038 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
2039 member.PrependChild (targets);
2040 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
2041 AppendElementAttributeText (targets, "Target", "Type",
2042 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
2045 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
2046 IList<TypeReference> constraints = gp.Constraints;
2047 if (constraints.Count == 0)
2048 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
2050 foreach (TypeReference c in constraints)
2051 AppendElementAttributeText(targets, "Target", "Type",
2052 slashdocFormatter.GetDeclaration (c));
2056 private static bool GetFieldConstValue (FieldDefinition field, out string value)
2059 TypeDefinition type = field.DeclaringType.Resolve ();
2060 if (type != null && type.IsEnum) return false;
2062 if (type != null && type.IsGenericType ()) return false;
2063 if (!field.HasConstant)
2065 if (field.IsLiteral) {
2066 object val = field.Constant;
2067 if (val == null) value = "null";
2068 else if (val is Enum) value = val.ToString();
2069 else if (val is IFormattable) {
2070 value = ((IFormattable)val).ToString();
2072 value = "\"" + value + "\"";
2074 if (value != null && value != "")
2080 // XML HELPER FUNCTIONS
2082 internal static XmlElement WriteElement(XmlNode parent, string element, bool forceNewElement = false) {
2083 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
2084 if (ret == null || forceNewElement) {
2085 string[] path = element.Split('/');
2086 foreach (string p in path) {
2087 ret = (XmlElement)parent.SelectSingleNode(p);
2088 if (ret == null || forceNewElement) {
2090 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
2091 ename = ename.Substring(0, ename.IndexOf('['));
2092 ret = parent.OwnerDocument.CreateElement(ename);
2093 parent.AppendChild(ret);
2102 private static XmlElement WriteElementText(XmlNode parent, string element, string value, bool forceNewElement = false) {
2103 XmlElement node = WriteElement(parent, element, forceNewElement: forceNewElement);
2104 node.InnerText = value;
2108 static XmlElement AppendElementText (XmlNode parent, string element, string value)
2110 XmlElement n = parent.OwnerDocument.CreateElement (element);
2111 parent.AppendChild (n);
2112 n.InnerText = value;
2116 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
2118 XmlElement n = parent.OwnerDocument.CreateElement (element);
2119 parent.AppendChild (n);
2120 n.SetAttribute (attribute, value);
2124 internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
2126 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
2127 dest.AppendChild (copy);
2131 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
2132 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
2135 node = WriteElement(parent, element);
2136 node.InnerText = value;
2138 private static XmlElement WriteElementAttribute(XmlElement parent, string element, string attribute, string value, bool forceNewElement = false) {
2139 XmlElement node = WriteElement(parent, element, forceNewElement:forceNewElement);
2140 return WriteElementAttribute (parent, node, attribute, value);
2142 private static XmlElement WriteElementAttribute(XmlElement parent, XmlElement node, string attribute, string value) {
2143 if (node.GetAttribute (attribute) != value) {
2144 node.SetAttribute (attribute, value);
2148 internal static void ClearElement(XmlElement parent, string name) {
2149 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
2151 parent.RemoveChild(node);
2154 // DOCUMENTATION HELPER FUNCTIONS
2156 private void MakeDocNode (DocsNodeInfo info)
2158 List<GenericParameter> genericParams = info.GenericParameters;
2159 IList<ParameterDefinition> parameters = info.Parameters;
2160 TypeReference returntype = info.ReturnType;
2161 bool returnisreturn = info.ReturnIsReturn;
2162 XmlElement e = info.Node;
2163 bool addremarks = info.AddRemarks;
2165 WriteElementInitialText(e, "summary", "To be added.");
2167 if (parameters != null) {
2168 string[] values = new string [parameters.Count];
2169 for (int i = 0; i < values.Length; ++i)
2170 values [i] = parameters [i].Name;
2171 UpdateParameters (e, "param", values);
2174 if (genericParams != null) {
2175 string[] values = new string [genericParams.Count];
2176 for (int i = 0; i < values.Length; ++i)
2177 values [i] = genericParams [i].Name;
2178 UpdateParameters (e, "typeparam", values);
2181 string retnodename = null;
2182 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
2183 retnodename = returnisreturn ? "returns" : "value";
2184 string retnodename_other = !returnisreturn ? "returns" : "value";
2186 // If it has a returns node instead of a value node, change its name.
2187 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
2188 if (retother != null) {
2189 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
2190 foreach (XmlNode node in retother)
2191 retnode.AppendChild(node.CloneNode(true));
2192 e.ReplaceChild(retnode, retother);
2194 WriteElementInitialText(e, retnodename, "To be added.");
2197 ClearElement(e, "returns");
2198 ClearElement(e, "value");
2202 WriteElementInitialText(e, "remarks", "To be added.");
2204 if (exceptions.HasValue && info.Member != null &&
2205 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
2206 UpdateExceptions (e, info.Member);
2209 foreach (DocumentationImporter importer in importers)
2210 importer.ImportDocumentation (info);
2212 OrderDocsNodes (e, e.ChildNodes);
2213 NormalizeWhitespace(e);
2216 static readonly string[] DocsNodeOrder = {
2217 "typeparam", "param", "summary", "returns", "value", "remarks",
2220 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
2222 ReorderNodes (docs, children, DocsNodeOrder);
2226 private void UpdateParameters (XmlElement e, string element, string[] values)
2228 if (values != null) {
2229 XmlNode[] paramnodes = new XmlNode[values.Length];
2231 // Some documentation had param nodes with leading spaces.
2232 foreach (XmlElement paramnode in e.SelectNodes(element)){
2233 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
2236 // If a member has only one parameter, we can track changes to
2237 // the name of the parameter easily.
2238 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
2239 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
2242 bool reinsert = false;
2244 // Pick out existing and still-valid param nodes, and
2245 // create nodes for parameters not in the file.
2246 Hashtable seenParams = new Hashtable();
2247 for (int pi = 0; pi < values.Length; pi++) {
2248 string p = values [pi];
2251 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
2252 if (paramnodes[pi] != null) continue;
2254 XmlElement pe = e.OwnerDocument.CreateElement(element);
2255 pe.SetAttribute("name", p);
2256 pe.InnerText = "To be added.";
2257 paramnodes[pi] = pe;
2261 // Remove parameters that no longer exist and check all params are in the right order.
2263 MyXmlNodeList todelete = new MyXmlNodeList ();
2264 foreach (XmlElement paramnode in e.SelectNodes(element)) {
2265 string name = paramnode.GetAttribute("name");
2266 if (!seenParams.ContainsKey(name)) {
2267 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2268 Warning ("The following param node can only be deleted if the --delete option is given: ");
2269 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
2271 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
2272 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2276 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
2277 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2278 e.ParentNode.Attributes ["MemberName"].Value,
2281 Warning ("\tValue={0}", paramnode.OuterXml);
2283 todelete.Add (paramnode);
2288 if ((int)seenParams[name] != idx)
2294 foreach (XmlNode n in todelete) {
2295 n.ParentNode.RemoveChild (n);
2298 // Re-insert the parameter nodes at the top of the doc section.
2300 for (int pi = values.Length-1; pi >= 0; pi--)
2301 e.PrependChild(paramnodes[pi]);
2303 // Clear all existing param nodes
2304 foreach (XmlNode paramnode in e.SelectNodes(element)) {
2305 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2306 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
2307 Console.WriteLine(paramnode.OuterXml);
2309 paramnode.ParentNode.RemoveChild(paramnode);
2315 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
2317 string existingName = pe.GetAttribute ("name");
2318 pe.SetAttribute ("name", newName);
2319 if (existingName == newName)
2321 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
2322 if (paramref.GetAttribute ("name").Trim () == existingName)
2323 paramref.SetAttribute ("name", newName);
2326 class CrefComparer : XmlNodeComparer {
2328 public CrefComparer ()
2332 public override int Compare (XmlNode x, XmlNode y)
2334 string xType = x.Attributes ["cref"].Value;
2335 string yType = y.Attributes ["cref"].Value;
2336 string xNamespace = GetNamespace (xType);
2337 string yNamespace = GetNamespace (yType);
2339 int c = xNamespace.CompareTo (yNamespace);
2342 return xType.CompareTo (yType);
2345 static string GetNamespace (string type)
2347 int n = type.LastIndexOf ('.');
2349 return type.Substring (0, n);
2350 return string.Empty;
2354 private void UpdateExceptions (XmlNode docs, MemberReference member)
2356 string indent = new string (' ', 10);
2357 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
2358 string cref = slashdocFormatter.GetDeclaration (source.Exception);
2359 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
2362 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
2363 e.SetAttribute ("cref", cref);
2364 e.InnerXml = "To be added; from:\n" + indent + "<see cref=\"" +
2365 string.Join ("\" />,\n" + indent + "<see cref=\"",
2366 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
2367 .OrderBy (s => s)) +
2369 docs.AppendChild (e);
2371 SortXmlNodes (docs, docs.SelectNodes ("exception"),
2372 new CrefComparer ());
2375 private static void NormalizeWhitespace(XmlElement e) {
2376 // Remove all text and whitespace nodes from the element so it
2377 // is outputted with nice indentation and no blank lines.
2378 ArrayList deleteNodes = new ArrayList();
2379 foreach (XmlNode n in e)
2380 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2382 foreach (XmlNode n in deleteNodes)
2383 n.ParentNode.RemoveChild(n);
2386 private bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
2388 TypeDefinition type = member as TypeDefinition;
2390 type = member.DeclaringType as TypeDefinition;
2392 var versions = new string[] { GetAssemblyVersion (type.Module.Assembly) };
2394 if (root.LocalName == "AssemblyInfo")
2395 return UpdateAssemblyVersionForAssemblyInfo (root, root.ParentNode as XmlElement, versions, add: true);
2397 return UpdateAssemblyVersions (root, type.Module.Assembly, versions, add);
2400 private static string GetAssemblyVersion (AssemblyDefinition assembly)
2402 return assembly.Name.Version.ToString();
2405 private bool UpdateAssemblyVersions(XmlElement root, AssemblyDefinition assembly, string[] assemblyVersions, bool add)
2410 XmlElement av = (XmlElement) root.SelectSingleNode ("AssemblyVersions");
2412 // AssemblyVersions is not part of the spec
2413 root.RemoveChild (av);
2416 string oldNodeFilter = "AssemblyInfo[not(@apistyle) or @apistyle='classic']";
2417 string newNodeFilter = "AssemblyInfo[@apistyle='unified']";
2418 string thisNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? newNodeFilter : oldNodeFilter;
2419 string thatNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? oldNodeFilter : newNodeFilter;
2421 XmlElement e = (XmlElement) root.SelectSingleNode (thisNodeFilter);
2423 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2425 if (MDocUpdater.HasDroppedNamespace (assembly)) {
2426 e.AddApiStyle (ApiStyle.Unified);
2429 root.AppendChild(e);
2432 var thatNode = (XmlElement) root.SelectSingleNode (thatNodeFilter);
2433 if (MDocUpdater.HasDroppedNamespace (assembly) && thatNode != null) {
2434 // there's a classic node, we should add apistyles
2435 e.AddApiStyle (ApiStyle.Unified);
2436 thatNode.AddApiStyle (ApiStyle.Classic);
2439 return UpdateAssemblyVersionForAssemblyInfo (e, root, assemblyVersions, add);
2442 static bool UpdateAssemblyVersionForAssemblyInfo (XmlElement e, XmlElement root, string[] assemblyVersions, bool add)
2444 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().Where (v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0).ToList ();
2445 // matches.Count > 0 && add: ignore -- already present
2446 if (matches.Count > 0 && !add) {
2447 foreach (XmlNode c in matches)
2450 else if (matches.Count == 0 && add) {
2451 foreach (string sv in assemblyVersions) {
2452 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2458 // matches.Count == 0 && !add: ignore -- already not present
2459 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2460 SortXmlNodes (e, avs, new VersionComparer ());
2462 bool anyNodesLeft = avs.Count != 0;
2463 if (!anyNodesLeft) {
2464 e.ParentNode.RemoveChild (e);
2466 return anyNodesLeft;
2469 // FIXME: get TypeReferences instead of string comparison?
2470 private static string[] IgnorableAttributes = {
2471 // Security related attributes
2472 "System.Reflection.AssemblyKeyFileAttribute",
2473 "System.Reflection.AssemblyDelaySignAttribute",
2474 // Present in @RefType
2475 "System.Runtime.InteropServices.OutAttribute",
2476 // For naming the indexer to use when not using indexers
2477 "System.Reflection.DefaultMemberAttribute",
2478 // for decimal constants
2479 "System.Runtime.CompilerServices.DecimalConstantAttribute",
2480 // compiler generated code
2481 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
2482 // more compiler generated code, e.g. iterator methods
2483 "System.Diagnostics.DebuggerHiddenAttribute",
2484 "System.Runtime.CompilerServices.FixedBufferAttribute",
2485 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
2486 // extension methods
2487 "System.Runtime.CompilerServices.ExtensionAttribute",
2488 // Used to differentiate 'object' from C#4 'dynamic'
2489 "System.Runtime.CompilerServices.DynamicAttribute",
2492 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes, TypeReference t=null)
2494 if (!attributes.Any ()) {
2495 ClearElement (root, "Attributes");
2499 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2503 e = root.OwnerDocument.CreateElement("Attributes");
2505 foreach (string attribute in attributes) {
2506 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2509 WriteElementText(ae, "AttributeName", attribute);
2512 if (e.ParentNode == null)
2513 root.AppendChild(e);
2515 NormalizeWhitespace(e);
2518 public static string MakeAttributesValueString (object v, TypeReference valueType)
2520 var formatters = new [] {
2521 new AttributeValueFormatter (),
2522 new ApplePlatformEnumFormatter (),
2523 new StandardFlagsEnumFormatter (),
2524 new DefaultAttributeValueFormatter (),
2527 ResolvedTypeInfo type = new ResolvedTypeInfo (valueType);
2528 foreach (var formatter in formatters) {
2529 string formattedValue;
2530 if (formatter.TryFormatValue (v, type, out formattedValue)) {
2531 return formattedValue;
2535 // this should never occur because the DefaultAttributeValueFormatter will always
2536 // successfully format the value ... but this is needed to satisfy the compiler :)
2537 throw new InvalidDataException (string.Format ("Unable to format attribute value ({0})", v.ToString ()));
2540 internal static IDictionary<long, string> GetEnumerationValues (TypeDefinition type)
2542 var values = new Dictionary<long, string> ();
2544 (from f in type.Fields
2545 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
2547 values [ToInt64 (f.Constant)] = f.Name;
2552 internal static long ToInt64 (object value)
2555 return (long) (ulong) value;
2556 return Convert.ToInt64 (value);
2559 private void MakeParameters (XmlElement root, MemberReference member, IList<ParameterDefinition> parameters, bool shouldDuplicateWithNew=false)
2561 XmlElement e = WriteElement(root, "Parameters");
2564 foreach (ParameterDefinition p in parameters) {
2568 var ptype = GetDocParameterType (p.ParameterType);
2569 var newPType = ptype;
2571 if (MDocUpdater.SwitchingToMagicTypes) {
2572 newPType = NativeTypeManager.ConvertFromNativeType (ptype);
2575 // now find the existing node, if it's there so we can reuse it.
2576 var nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2577 .Cast<XmlElement> ().Where (x => x.GetAttribute ("Name") == p.Name)
2580 if (nodes.Count () == 0) {
2581 // wasn't found, let's make sure it wasn't just cause the param name was changed
2582 nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2583 .Cast<XmlElement> ()
2584 .Skip (i) // this makes sure we don't inadvertently "reuse" nodes when adding new ones
2585 .Where (x => x.GetAttribute ("Name") != p.Name && (x.GetAttribute ("Type") == ptype || x.GetAttribute ("Type") == newPType))
2586 .Take(1) // there might be more than one that meets this parameter ... only take the first.
2591 x => x.GetAttribute ("Type") == ptype,
2592 x => x.SetAttribute ("Type", ptype),
2594 pe = root.OwnerDocument.CreateElement ("Parameter");
2597 pe.SetAttribute ("Name", p.Name);
2598 pe.SetAttribute ("Type", ptype);
2599 if (p.ParameterType is ByReferenceType) {
2601 pe.SetAttribute ("RefType", "out");
2603 pe.SetAttribute ("RefType", "ref");
2606 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
2615 private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams, MemberReference member, bool shouldDuplicateWithNew)
2617 if (typeParams == null || typeParams.Count == 0) {
2618 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2620 root.RemoveChild (f);
2623 XmlElement e = WriteElement(root, "TypeParameters");
2625 var nodes = e.SelectNodes ("TypeParameter").Cast<XmlElement> ().ToArray ();
2627 foreach (GenericParameter t in typeParams) {
2629 IList<TypeReference> constraints = t.Constraints;
2630 GenericParameterAttributes attrs = t.Attributes;
2636 var baseType = e.SelectSingleNode("BaseTypeName");
2637 // TODO: should this comparison take into account BaseTypeName?
2638 return x.GetAttribute("Name") == t.Name;
2640 x => {}, // no additional action required
2643 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2645 pe.SetAttribute("Name", t.Name);
2646 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""), t.DeclaringType);
2647 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2648 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2656 ce = root.OwnerDocument.CreateElement ("Constraints");
2658 pe.AppendChild (ce);
2659 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2660 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2661 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2662 AppendElementText (ce, "ParameterAttribute", "Covariant");
2663 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2664 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2665 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2666 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2667 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2668 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2669 foreach (TypeReference c in constraints) {
2670 TypeDefinition cd = c.Resolve ();
2671 AppendElementText (ce,
2672 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2673 GetDocTypeFullName (c));
2682 private void MakeParameters (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew)
2684 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2685 MakeParameters (root, mi, ((MethodDefinition)mi).Parameters, shouldDuplicateWithNew);
2686 else if (mi is MethodDefinition) {
2687 MethodDefinition mb = (MethodDefinition) mi;
2688 IList<ParameterDefinition> parameters = mb.Parameters;
2689 MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
2690 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2691 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2692 p.SetAttribute ("RefType", "this");
2695 else if (mi is PropertyDefinition) {
2696 IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
2697 if (parameters.Count > 0)
2698 MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
2702 else if (mi is FieldDefinition) return;
2703 else if (mi is EventDefinition) return;
2704 else throw new ArgumentException();
2707 internal static string GetDocParameterType (TypeReference type)
2709 return GetDocTypeFullName (type).Replace ("@", "&");
2712 private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes, bool shouldDuplicateWithNew=false)
2714 XmlElement e = WriteElement(root, "ReturnValue");
2715 var valueToUse = GetDocTypeFullName (type);
2717 AddXmlNode (e.SelectNodes("ReturnType").Cast<XmlElement> ().ToArray (),
2718 x => x.InnerText == valueToUse,
2719 x => x.InnerText = valueToUse,
2721 var newNode = WriteElementText(e, "ReturnType", valueToUse, forceNewElement: true);
2722 if (attributes != null)
2723 MakeAttributes(e, GetCustomAttributes (attributes, ""), type);
2730 private void MakeReturnValue (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew=false)
2732 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2734 else if (mi is MethodDefinition)
2735 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes, shouldDuplicateWithNew);
2736 else if (mi is PropertyDefinition)
2737 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null, shouldDuplicateWithNew);
2738 else if (mi is FieldDefinition)
2739 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null, shouldDuplicateWithNew);
2740 else if (mi is EventDefinition)
2741 MakeReturnValue (root, ((EventDefinition)mi).EventType, null, shouldDuplicateWithNew);
2743 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2746 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2748 MemberReference mi = info.Member;
2749 if (mi is TypeDefinition) return null;
2751 string sigs = memberFormatters [0].GetDeclaration (mi);
2752 if (sigs == null) return null; // not publicly visible
2754 // no documentation for property/event accessors. Is there a better way of doing this?
2755 if (mi.Name.StartsWith("get_")) return null;
2756 if (mi.Name.StartsWith("set_")) return null;
2757 if (mi.Name.StartsWith("add_")) return null;
2758 if (mi.Name.StartsWith("remove_")) return null;
2759 if (mi.Name.StartsWith("raise_")) return null;
2761 XmlElement me = doc.CreateElement("Member");
2762 me.SetAttribute("MemberName", GetMemberName (mi));
2766 if (exceptions.HasValue &&
2767 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2768 UpdateExceptions (info.Node, info.Member);
2770 if (since != null) {
2771 XmlNode docs = me.SelectSingleNode("Docs");
2772 docs.AppendChild (CreateSinceNode (doc));
2778 internal static string GetMemberName (MemberReference mi)
2780 MethodDefinition mb = mi as MethodDefinition;
2782 PropertyDefinition pi = mi as PropertyDefinition;
2785 return DocUtils.GetPropertyName (pi);
2787 StringBuilder sb = new StringBuilder (mi.Name.Length);
2788 if (!DocUtils.IsExplicitlyImplemented (mb))
2789 sb.Append (mi.Name);
2791 TypeReference iface;
2792 MethodReference ifaceMethod;
2793 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2794 sb.Append (GetDocTypeFullName (iface));
2796 sb.Append (ifaceMethod.Name);
2798 if (mb.IsGenericMethod ()) {
2799 IList<GenericParameter> typeParams = mb.GenericParameters;
2800 if (typeParams.Count > 0) {
2802 sb.Append (typeParams [0].Name);
2803 for (int i = 1; i < typeParams.Count; ++i)
2804 sb.Append (",").Append (typeParams [i].Name);
2808 return sb.ToString ();
2811 /// SIGNATURE GENERATION FUNCTIONS
2812 internal static bool IsPrivate (MemberReference mi)
2814 return memberFormatters [0].GetDeclaration (mi) == null;
2817 internal static string GetMemberType (MemberReference mi)
2819 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2820 return "Constructor";
2821 if (mi is MethodDefinition)
2823 if (mi is PropertyDefinition)
2825 if (mi is FieldDefinition)
2827 if (mi is EventDefinition)
2829 throw new ArgumentException();
2832 private static string GetDocTypeName (TypeReference type)
2834 return docTypeFormatter.GetName (type);
2837 internal static string GetDocTypeFullName (TypeReference type)
2839 return DocTypeFullMemberFormatter.Default.GetName (type);
2842 internal static string GetXPathForMember (DocumentationMember member)
2844 StringBuilder xpath = new StringBuilder ();
2845 xpath.Append ("//Members/Member[@MemberName=\"")
2846 .Append (member.MemberName)
2848 if (member.Parameters != null && member.Parameters.Count > 0) {
2849 xpath.Append ("/Parameters[count(Parameter) = ")
2850 .Append (member.Parameters.Count);
2851 for (int i = 0; i < member.Parameters.Count; ++i) {
2852 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2853 xpath.Append (member.Parameters [i]);
2854 xpath.Append ("\"");
2856 xpath.Append ("]/..");
2858 return xpath.ToString ();
2861 public static string GetXPathForMember (XPathNavigator member)
2863 StringBuilder xpath = new StringBuilder ();
2864 xpath.Append ("//Type[@FullName=\"")
2865 .Append (member.SelectSingleNode ("../../@FullName").Value)
2867 xpath.Append ("Members/Member[@MemberName=\"")
2868 .Append (member.SelectSingleNode ("@MemberName").Value)
2870 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2871 if (parameters.Count > 0) {
2872 xpath.Append ("/Parameters[count(Parameter) = ")
2873 .Append (parameters.Count);
2875 while (parameters.MoveNext ()) {
2877 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2878 xpath.Append (parameters.Current.Value);
2879 xpath.Append ("\"");
2881 xpath.Append ("]/..");
2883 return xpath.ToString ();
2886 public static string GetXPathForMember (MemberReference member)
2888 StringBuilder xpath = new StringBuilder ();
2889 xpath.Append ("//Type[@FullName=\"")
2890 .Append (member.DeclaringType.FullName)
2892 xpath.Append ("Members/Member[@MemberName=\"")
2893 .Append (GetMemberName (member))
2896 IList<ParameterDefinition> parameters = null;
2897 if (member is MethodDefinition)
2898 parameters = ((MethodDefinition) member).Parameters;
2899 else if (member is PropertyDefinition) {
2900 parameters = ((PropertyDefinition) member).Parameters;
2902 if (parameters != null && parameters.Count > 0) {
2903 xpath.Append ("/Parameters[count(Parameter) = ")
2904 .Append (parameters.Count);
2905 for (int i = 0; i < parameters.Count; ++i) {
2906 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2907 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2908 xpath.Append ("\"");
2910 xpath.Append ("]/..");
2912 return xpath.ToString ();
2916 static class CecilExtensions {
2917 public static string GetDeclaringType(this CustomAttribute attribute)
2919 var type = attribute.Constructor.DeclaringType;
2920 var typeName = type.FullName;
2922 string translatedType = NativeTypeManager.GetTranslatedName (type);
2923 return translatedType;
2926 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
2928 foreach (var c in type.Methods.Where (m => m.IsConstructor))
2929 yield return (MemberReference) c;
2930 foreach (var e in type.Events)
2931 yield return (MemberReference) e;
2932 foreach (var f in type.Fields)
2933 yield return (MemberReference) f;
2934 foreach (var m in type.Methods.Where (m => !m.IsConstructor))
2935 yield return (MemberReference) m;
2936 foreach (var t in type.NestedTypes)
2937 yield return (MemberReference) t;
2938 foreach (var p in type.Properties)
2939 yield return (MemberReference) p;
2942 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
2944 return GetMembers (type).Where (m => m.Name == member);
2947 public static MemberReference GetMember (this TypeDefinition type, string member)
2949 return GetMembers (type, member).EnsureZeroOrOne ();
2952 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2954 if (source.Count () > 1)
2955 throw new InvalidOperationException ("too many matches");
2956 return source.FirstOrDefault ();
2959 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2962 .Where (m => m.Name == method)
2963 .EnsureZeroOrOne ();
2966 public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
2968 TypeDefinition def = type as TypeDefinition;
2970 return new MemberReference [0];
2971 CustomAttribute defMemberAttr = def.CustomAttributes
2972 .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
2973 if (defMemberAttr == null)
2974 return new MemberReference [0];
2975 string name = (string) defMemberAttr.ConstructorArguments [0].Value;
2976 return def.Properties
2977 .Where (p => p.Name == name)
2978 .Select (p => (MemberReference) p);
2981 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2983 return assembly.Modules.SelectMany (md => md.GetAllTypes ());
2986 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2988 return GetTypes (assembly)
2989 .Where (td => td.FullName == type)
2990 .EnsureZeroOrOne ();
2993 public static bool IsGenericType (this TypeReference type)
2995 return type.GenericParameters.Count > 0;
2998 public static bool IsGenericMethod (this MethodReference method)
3000 return method.GenericParameters.Count > 0;
3003 public static MemberReference Resolve (this MemberReference member)
3005 FieldReference fr = member as FieldReference;
3007 return fr.Resolve ();
3008 MethodReference mr = member as MethodReference;
3010 return mr.Resolve ();
3011 TypeReference tr = member as TypeReference;
3013 return tr.Resolve ();
3014 PropertyReference pr = member as PropertyReference;
3017 EventReference er = member as EventReference;
3020 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
3023 public static TypeReference GetUnderlyingType (this TypeDefinition type)
3027 return type.Fields.First (f => f.Name == "value__").FieldType;
3030 public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
3032 return self.Types.SelectMany (t => t.GetAllTypes ());
3035 static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
3039 if (!self.HasNestedTypes)
3042 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
3052 static class DocUtils {
3054 public static bool DoesNotHaveApiStyle(this XmlElement element, ApiStyle style) {
3055 string styleString = style.ToString ().ToLowerInvariant ();
3056 string apistylevalue = element.GetAttribute ("apistyle");
3057 return apistylevalue != styleString || string.IsNullOrWhiteSpace(apistylevalue);
3059 public static bool HasApiStyle(this XmlElement element, ApiStyle style) {
3060 string styleString = style.ToString ().ToLowerInvariant ();
3061 return element.GetAttribute ("apistyle") == styleString;
3063 public static bool HasApiStyle(this XmlNode node, ApiStyle style)
3065 var attribute = node.Attributes ["apistyle"];
3066 return attribute != null && attribute.Value == style.ToString ().ToLowerInvariant ();
3068 public static void AddApiStyle(this XmlElement element, ApiStyle style) {
3069 string styleString = style.ToString ().ToLowerInvariant ();
3070 var existingValue = element.GetAttribute ("apistyle");
3071 if (string.IsNullOrWhiteSpace (existingValue) || existingValue != styleString) {
3072 element.SetAttribute ("apistyle", styleString);
3075 // Propagate the API style up to the membernode if necessary
3076 if (element.LocalName == "AssemblyInfo" && element.ParentNode != null && element.ParentNode.LocalName == "Member") {
3077 var member = element.ParentNode;
3078 var unifiedAssemblyNode = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']");
3079 var classicAssemblyNode = member.SelectSingleNode ("AssemblyInfo[not(@apistyle) or @apistyle='classic']");
3081 var parentAttribute = element.ParentNode.Attributes ["apistyle"];
3082 Action removeStyle = () => element.ParentNode.Attributes.Remove (parentAttribute);
3083 Action propagateStyle = () => {
3084 if (parentAttribute == null) {
3085 // if it doesn't have the attribute, then add it
3086 parentAttribute = element.OwnerDocument.CreateAttribute ("apistyle");
3087 parentAttribute.Value = styleString;
3088 element.ParentNode.Attributes.Append (parentAttribute);
3092 if ((style == ApiStyle.Classic && unifiedAssemblyNode != null) || (style == ApiStyle.Unified && classicAssemblyNode != null))
3098 public static void AddApiStyle (this XmlNode node, ApiStyle style)
3100 string styleString = style.ToString ().ToLowerInvariant ();
3101 var existingAttribute = node.Attributes ["apistyle"];
3102 if (existingAttribute == null) {
3103 existingAttribute = node.OwnerDocument.CreateAttribute ("apistyle");
3104 node.Attributes.Append (existingAttribute);
3106 existingAttribute.Value = styleString;
3108 public static void RemoveApiStyle (this XmlElement element, ApiStyle style)
3110 string styleString = style.ToString ().ToLowerInvariant ();
3111 string existingValue = element.GetAttribute ("apistyle");
3112 if (string.IsNullOrWhiteSpace (existingValue) || existingValue == styleString) {
3113 element.RemoveAttribute ("apistyle");
3116 public static void RemoveApiStyle (this XmlNode node, ApiStyle style)
3118 var styleAttribute = node.Attributes ["apistyle"];
3119 if (styleAttribute != null && styleAttribute.Value == style.ToString ().ToLowerInvariant ()) {
3120 node.Attributes.Remove (styleAttribute);
3124 public static bool IsExplicitlyImplemented (MethodDefinition method)
3126 return method.IsPrivate && method.IsFinal && method.IsVirtual;
3129 public static string GetTypeDotMember (string name)
3131 int startType, startMethod;
3132 startType = startMethod = -1;
3133 for (int i = 0; i < name.Length; ++i) {
3134 if (name [i] == '.') {
3135 startType = startMethod;
3139 return name.Substring (startType+1);
3142 public static string GetMember (string name)
3144 int i = name.LastIndexOf ('.');
3147 return name.Substring (i+1);
3150 public static void GetInfoForExplicitlyImplementedMethod (
3151 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
3155 if (method.Overrides.Count != 1)
3156 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
3157 iface = method.Overrides [0].DeclaringType;
3158 ifaceMethod = method.Overrides [0];
3161 public static string GetPropertyName (PropertyDefinition pi)
3163 // Issue: (g)mcs-generated assemblies that explicitly implement
3164 // properties don't specify the full namespace, just the
3165 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
3166 MethodDefinition method = pi.GetMethod;
3168 method = pi.SetMethod;
3169 if (!IsExplicitlyImplemented (method))
3172 // Need to determine appropriate namespace for this member.
3173 TypeReference iface;
3174 MethodReference ifaceMethod;
3175 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3176 return string.Join (".", new string[]{
3177 DocTypeFullMemberFormatter.Default.GetName (iface),
3178 GetMember (pi.Name)});
3181 public static string GetNamespace (TypeReference type)
3183 if (type.GetElementType ().IsNested)
3184 type = type.GetElementType ();
3185 while (type != null && type.IsNested)
3186 type = type.DeclaringType;
3188 return string.Empty;
3190 string typeNS = type.Namespace;
3192 // first, make sure this isn't a type reference to another assembly/module
3194 bool isInAssembly = MDocUpdater.IsInAssemblies(type.Module.Name);
3195 if (isInAssembly && !typeNS.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (type)) {
3196 typeNS = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typeNS);
3201 public static string PathCombine (string dir, string path)
3207 return Path.Combine (dir, path);
3210 public static bool IsExtensionMethod (MethodDefinition method)
3213 method.CustomAttributes
3214 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
3215 && method.DeclaringType.CustomAttributes
3216 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
3219 public static bool IsDelegate (TypeDefinition type)
3221 TypeReference baseRef = type.BaseType;
3222 if (baseRef == null)
3224 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
3225 baseRef.FullName == "System.MulticastDelegate";
3228 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
3230 List<TypeReference> decls = new List<TypeReference> ();
3232 while (type.DeclaringType != null) {
3233 decls.Add (type.DeclaringType);
3234 type = type.DeclaringType;
3240 public static int GetGenericArgumentCount (TypeReference type)
3242 GenericInstanceType inst = type as GenericInstanceType;
3244 ? inst.GenericArguments.Count
3245 : type.GenericParameters.Count;
3248 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
3250 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
3251 List<TypeReference> userInterfaces = new List<TypeReference> ();
3252 foreach (TypeReference iface in type.Interfaces) {
3253 TypeReference lookup = iface.Resolve () ?? iface;
3254 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
3255 userInterfaces.Add (iface);
3257 return userInterfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()));
3260 private static string GetQualifiedTypeName (TypeReference type)
3262 return "[" + type.Scope.Name + "]" + type.FullName;
3265 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
3267 HashSet<string> inheritedInterfaces = new HashSet<string> ();
3268 Action<TypeDefinition> a = null;
3270 if (t == null) return;
3271 foreach (TypeReference r in t.Interfaces) {
3272 inheritedInterfaces.Add (GetQualifiedTypeName (r));
3276 TypeReference baseRef = type.BaseType;
3277 while (baseRef != null) {
3278 TypeDefinition baseDef = baseRef.Resolve ();
3279 if (baseDef != null) {
3281 baseRef = baseDef.BaseType;
3286 foreach (TypeReference r in type.Interfaces)
3288 return inheritedInterfaces;
3292 class DocsNodeInfo {
3293 public DocsNodeInfo (XmlElement node)
3298 public DocsNodeInfo (XmlElement node, TypeDefinition type)
3304 public DocsNodeInfo (XmlElement node, MemberReference member)
3307 SetMemberInfo (member);
3310 void SetType (TypeDefinition type)
3313 throw new ArgumentNullException ("type");
3315 GenericParameters = new List<GenericParameter> (type.GenericParameters);
3316 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
3317 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
3318 for (int i = 0; i < declTypes.Count - 1; ++i) {
3319 int remove = System.Math.Min (maxGenArgs,
3320 DocUtils.GetGenericArgumentCount (declTypes [i]));
3321 maxGenArgs -= remove;
3322 while (remove-- > 0)
3323 GenericParameters.RemoveAt (0);
3325 if (DocUtils.IsDelegate (type)) {
3326 Parameters = type.GetMethod("Invoke").Parameters;
3327 ReturnType = type.GetMethod("Invoke").ReturnType;
3328 ReturnIsReturn = true;
3332 void SetMemberInfo (MemberReference member)
3335 throw new ArgumentNullException ("member");
3336 ReturnIsReturn = true;
3340 if (member is MethodReference ) {
3341 MethodReference mr = (MethodReference) member;
3342 Parameters = mr.Parameters;
3343 if (mr.IsGenericMethod ()) {
3344 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
3347 else if (member is PropertyDefinition) {
3348 Parameters = ((PropertyDefinition) member).Parameters;
3351 if (member is MethodDefinition) {
3352 ReturnType = ((MethodDefinition) member).ReturnType;
3353 } else if (member is PropertyDefinition) {
3354 ReturnType = ((PropertyDefinition) member).PropertyType;
3355 ReturnIsReturn = false;
3358 // no remarks section for enum members
3359 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
3363 public TypeReference ReturnType;
3364 public List<GenericParameter> GenericParameters;
3365 public IList<ParameterDefinition> Parameters;
3366 public bool ReturnIsReturn;
3367 public XmlElement Node;
3368 public bool AddRemarks = true;
3369 public MemberReference Member;
3370 public TypeDefinition Type;
3372 public override string ToString ()
3374 return string.Format ("{0} - {1} - {2}", Type, Member, Node == null ? "no xml" : "with xml");
3378 class DocumentationEnumerator {
3380 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3382 return GetDocumentationTypes (assembly, forTypes, null);
3385 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3387 foreach (TypeDefinition type in assembly.GetTypes()) {
3388 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
3390 if (seen != null && seen.Contains (type.FullName))
3393 foreach (TypeDefinition nested in type.NestedTypes)
3394 yield return nested;
3398 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3400 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
3401 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
3402 oldmember.RemoveAttribute ("__monodocer-seen__");
3405 MemberReference m = GetMember (type, new DocumentationMember (oldmember));
3407 yield return new DocsNodeInfo (oldmember);
3410 yield return new DocsNodeInfo (oldmember, m);
3415 protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
3417 string membertype = member.MemberType;
3419 string returntype = member.ReturnType;
3421 string docName = member.MemberName;
3423 string[] docTypeParams = GetTypeParameters (docName, member.TypeParameters);
3425 // If we're using 'magic types', then we might get false positives ... in those cases, we keep searching
3426 MemberReference likelyCandidate = null;
3428 // Loop through all members in this type with the same name
3429 var reflectedMembers = GetReflectionMembers (type, docName).ToArray ();
3430 foreach (MemberReference mi in reflectedMembers) {
3431 bool matchedMagicType = false;
3432 if (mi is TypeDefinition) continue;
3433 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
3435 if (MDocUpdater.IsPrivate (mi))
3438 IList<ParameterDefinition> pis = null;
3439 string[] typeParams = null;
3440 if (mi is MethodDefinition) {
3441 MethodDefinition mb = (MethodDefinition) mi;
3442 pis = mb.Parameters;
3443 if (mb.IsGenericMethod ()) {
3444 IList<GenericParameter> args = mb.GenericParameters;
3445 typeParams = args.Select (p => p.Name).ToArray ();
3448 else if (mi is PropertyDefinition)
3449 pis = ((PropertyDefinition)mi).Parameters;
3451 // check type parameters
3452 int methodTcount = member.TypeParameters == null ? 0 : member.TypeParameters.Count;
3453 int reflectionTcount = typeParams == null ? 0 : typeParams.Length;
3454 if (methodTcount != reflectionTcount)
3457 // check member parameters
3458 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
3459 int pcount = pis == null ? 0 : pis.Count;
3460 if (mcount != pcount)
3463 MethodDefinition mDef = mi as MethodDefinition;
3464 if (mDef != null && !mDef.IsConstructor) {
3465 // Casting operators can overload based on return type.
3466 string rtype = GetReplacedString (
3467 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
3468 typeParams, docTypeParams);
3469 string originalRType = rtype;
3470 if (MDocUpdater.SwitchingToMagicTypes) {
3471 rtype = NativeTypeManager.ConvertFromNativeType (rtype);
3474 if ((returntype != rtype && originalRType == rtype) ||
3475 (MDocUpdater.SwitchingToMagicTypes && returntype != originalRType && returntype != rtype && originalRType != rtype)) {
3479 if (originalRType != rtype)
3480 matchedMagicType = true;
3486 for (int i = 0; i < pis.Count; i++) {
3487 string paramType = GetReplacedString (
3488 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
3489 typeParams, docTypeParams);
3491 // if magictypes, replace paramType to "classic value" ... so the comparison works
3492 string originalParamType = paramType;
3493 if (MDocUpdater.SwitchingToMagicTypes) {
3494 paramType = NativeTypeManager.ConvertFromNativeType (paramType);
3497 string xmlMemberType = member.Parameters [i];
3498 if ((!paramType.Equals(xmlMemberType) && paramType.Equals(originalParamType)) ||
3499 (MDocUpdater.SwitchingToMagicTypes && !originalParamType.Equals(xmlMemberType) && !paramType.Equals(xmlMemberType) && !paramType.Equals(originalParamType))) {
3501 // did not match ... if we're dropping the namespace, and the paramType has the dropped
3502 // namespace, we should see if it matches when added
3503 bool stillDoesntMatch = true;
3504 if (MDocUpdater.HasDroppedNamespace(type) && paramType.StartsWith (MDocUpdater.droppedNamespace)) {
3505 string withDroppedNs = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, xmlMemberType);
3507 stillDoesntMatch = withDroppedNs != paramType;
3510 if (stillDoesntMatch) {
3516 if (originalParamType != paramType)
3517 matchedMagicType = true;
3519 if (!good) continue;
3521 if (MDocUpdater.SwitchingToMagicTypes && likelyCandidate == null && matchedMagicType) {
3522 // 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
3523 likelyCandidate = mi;
3530 return likelyCandidate;
3533 static string[] GetTypeParameters (string docName, IEnumerable<string> knownParameters)
3535 if (docName [docName.Length-1] != '>')
3537 StringList types = new StringList ();
3538 int endToken = docName.Length-2;
3539 int i = docName.Length-2;
3541 if (docName [i] == ',' || docName [i] == '<') {
3542 types.Add (docName.Substring (i + 1, endToken - i));
3545 if (docName [i] == '<')
3550 var arrayTypes = types.ToArray ();
3552 if (knownParameters != null && knownParameters.Any () && arrayTypes.Length != knownParameters.Count ())
3553 return knownParameters.ToArray ();
3558 protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
3560 // In case of dropping the namespace, we have to remove the dropped NS
3561 // so that docName will match what's in the assembly/type
3562 if (MDocUpdater.HasDroppedNamespace (type) && docName.StartsWith(MDocUpdater.droppedNamespace + ".")) {
3563 int droppedNsLength = MDocUpdater.droppedNamespace.Length;
3564 docName = docName.Substring (droppedNsLength + 1, docName.Length - droppedNsLength - 1);
3567 // need to worry about 4 forms of //@MemberName values:
3568 // 1. "Normal" (non-generic) member names: GetEnumerator
3570 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
3571 // - try as-is, and try type.member (due to "kludge" for property
3573 // 3. "Normal" Generic member names: Sort<T> (CSC)
3574 // - need to remove generic parameters --> "Sort"
3575 // 4. Explicitly-implemented interface members for generic interfaces:
3576 // -- System.Collections.Generic.IEnumerable<T>.Current
3577 // - Try as-is, and try type.member, *keeping* the generic parameters.
3578 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
3579 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
3580 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
3581 // this as (1) or (2).
3582 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
3584 foreach (MemberReference mi in type.GetMembers (docName))
3586 if (CountChars (docName, '.') > 0)
3587 // might be a property; try only type.member instead of
3588 // namespace.type.member.
3589 foreach (MemberReference mi in
3590 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
3597 int startLt, startType, startMethod;
3598 startLt = startType = startMethod = -1;
3599 for (int i = 0; i < docName.Length; ++i) {
3600 switch (docName [i]) {
3609 if (numLt == 0 && (i + 1) < docName.Length)
3610 // there's another character in docName, so this <...> sequence is
3611 // probably part of a generic type -- case 4.
3615 startType = startMethod;
3621 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
3623 foreach (MemberReference mi in type.GetMembers (refName))
3627 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
3630 // If we _still_ haven't found it, we've hit another generic naming issue:
3631 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
3632 // explicitly-implemented METHOD names (not properties), e.g.
3633 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
3634 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
3635 // which the XML docs will contain.
3637 // Alas, we can't derive the Mono name from docName, so we need to iterate
3638 // over all member names, convert them into CSC format, and compare... :-(
3641 foreach (MemberReference mi in type.GetMembers ()) {
3642 if (MDocUpdater.GetMemberName (mi) == docName)
3647 static string GetReplacedString (string typeName, string[] from, string[] to)
3651 for (int i = 0; i < from.Length; ++i)
3652 typeName = typeName.Replace (from [i], to [i]);
3656 private static int CountChars (string s, char c)
3659 for (int i = 0; i < s.Length; ++i) {
3667 class EcmaDocumentationEnumerator : DocumentationEnumerator {
3672 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
3675 this.ecmadocs = ecmaDocs;
3678 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3680 HashSet<string> seen = new HashSet<string> ();
3681 return GetDocumentationTypes (assembly, forTypes, seen)
3682 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
3685 new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3688 while (ecmadocs.Read ()) {
3689 switch (ecmadocs.Name) {
3691 if (typeDepth == -1)
3692 typeDepth = ecmadocs.Depth;
3693 if (ecmadocs.NodeType != XmlNodeType.Element)
3695 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
3697 string typename = ecmadocs.GetAttribute ("FullName");
3698 string typename2 = MDocUpdater.GetTypeFileName (typename);
3699 if (forTypes != null &&
3700 forTypes.BinarySearch (typename) < 0 &&
3701 typename != typename2 &&
3702 forTypes.BinarySearch (typename2) < 0)
3705 if ((t = assembly.GetType (typename)) == null &&
3706 (t = assembly.GetType (typename2)) == null)
3708 seen.Add (typename);
3709 if (typename != typename2)
3710 seen.Add (typename2);
3711 Console.WriteLine (" Import: {0}", t.FullName);
3712 if (ecmadocs.Name != "Docs") {
3713 int depth = ecmadocs.Depth;
3714 while (ecmadocs.Read ()) {
3715 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
3719 if (!ecmadocs.IsStartElement ("Docs"))
3720 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
3730 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3732 return GetMembers (basefile, type)
3733 .Concat (base.GetDocumentationMembers (basefile, type));
3736 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
3738 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
3741 if (ecmadocs.IsEmptyElement)
3744 int membersDepth = ecmadocs.Depth;
3746 while (go && ecmadocs.Read ()) {
3747 switch (ecmadocs.Name) {
3749 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
3751 DocumentationMember dm = new DocumentationMember (ecmadocs);
3753 string xp = MDocUpdater.GetXPathForMember (dm);
3754 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
3756 if (oldmember == null) {
3757 m = GetMember (type, dm);
3759 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3760 type.FullName, dm.MemberSignatures ["C#"]);
3761 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
3764 // oldmember lookup may have failed due to type parameter renames.
3766 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
3767 if (oldmember == null) {
3768 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
3769 oldmember = basefile.CreateElement ("Member");
3770 oldmember.SetAttribute ("MemberName", dm.MemberName);
3771 members.AppendChild (oldmember);
3772 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
3773 XmlElement ms = basefile.CreateElement ("MemberSignature");
3774 ms.SetAttribute ("Language", key);
3775 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
3776 oldmember.AppendChild (ms);
3778 oldmember.SetAttribute ("__monodocer-seen__", "true");
3779 Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
3784 m = GetMember (type, new DocumentationMember (oldmember));
3786 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3787 type.FullName, dm.MemberSignatures ["C#"]);
3790 oldmember.SetAttribute ("__monodocer-seen__", "true");
3792 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
3793 if (ecmadocs.Name != "Docs")
3794 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
3799 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
3808 abstract class DocumentationImporter {
3810 public abstract void ImportDocumentation (DocsNodeInfo info);
3813 class MsxdocDocumentationImporter : DocumentationImporter {
3815 XmlDocument slashdocs;
3817 public MsxdocDocumentationImporter (string file)
3819 var xml = File.ReadAllText (file);
3821 // Ensure Unix line endings
3822 xml = xml.Replace ("\r", "");
3824 slashdocs = new XmlDocument();
3825 slashdocs.LoadXml (xml);
3828 public override void ImportDocumentation (DocsNodeInfo info)
3830 XmlNode elem = GetDocs (info.Member ?? info.Type);
3835 XmlElement e = info.Node;
3837 if (elem.SelectSingleNode("summary") != null)
3838 MDocUpdater.ClearElement(e, "summary");
3839 if (elem.SelectSingleNode("remarks") != null)
3840 MDocUpdater.ClearElement(e, "remarks");
3841 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3842 MDocUpdater.ClearElement(e, "value");
3843 MDocUpdater.ClearElement(e, "returns");
3846 foreach (XmlNode child in elem.ChildNodes) {
3847 switch (child.Name) {
3850 XmlAttribute name = child.Attributes ["name"];
3853 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3855 p2.InnerXml = child.InnerXml;
3858 // Occasionally XML documentation will use <returns/> on
3859 // properties, so let's try to normalize things.
3862 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3863 v.InnerXml = child.InnerXml;
3869 case "permission": {
3870 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3873 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3875 a = e.OwnerDocument.CreateElement (child.Name);
3876 a.SetAttribute ("cref", cref.Value);
3879 a.InnerXml = child.InnerXml;
3883 XmlAttribute cref = child.Attributes ["cref"];
3886 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3888 a = e.OwnerDocument.CreateElement ("altmember");
3889 a.SetAttribute ("cref", cref.Value);
3896 if (child.NodeType == XmlNodeType.Element &&
3897 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3900 MDocUpdater.CopyNode (child, e);
3907 private XmlNode GetDocs (MemberReference member)
3909 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3910 if (slashdocsig != null)
3911 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3916 class EcmaDocumentationImporter : DocumentationImporter {
3920 public EcmaDocumentationImporter (XmlReader ecmaDocs)
3922 this.ecmadocs = ecmaDocs;
3925 public override void ImportDocumentation (DocsNodeInfo info)
3927 if (!ecmadocs.IsStartElement ("Docs")) {
3931 XmlElement e = info.Node;
3933 int depth = ecmadocs.Depth;
3934 ecmadocs.ReadStartElement ("Docs");
3935 while (ecmadocs.Read ()) {
3936 if (ecmadocs.Name == "Docs") {
3937 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3940 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3942 if (!ecmadocs.IsStartElement ())
3944 switch (ecmadocs.Name) {
3947 string name = ecmadocs.GetAttribute ("name");
3950 XmlNode doc = e.SelectSingleNode (
3951 ecmadocs.Name + "[@name='" + name + "']");
3952 string value = ecmadocs.ReadInnerXml ();
3954 doc.InnerXml = value.Replace ("\r", "");
3961 string name = ecmadocs.Name;
3962 string cref = ecmadocs.GetAttribute ("cref");
3965 XmlNode doc = e.SelectSingleNode (
3966 ecmadocs.Name + "[@cref='" + cref + "']");
3967 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3969 doc.InnerXml = value;
3971 XmlElement n = e.OwnerDocument.CreateElement (name);
3972 n.SetAttribute ("cref", cref);
3979 string name = ecmadocs.Name;
3980 string xpath = ecmadocs.Name;
3981 StringList attributes = new StringList (ecmadocs.AttributeCount);
3982 if (ecmadocs.MoveToFirstAttribute ()) {
3984 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3985 } while (ecmadocs.MoveToNextAttribute ());
3986 ecmadocs.MoveToContent ();
3988 if (attributes.Count > 0) {
3989 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3991 XmlNode doc = e.SelectSingleNode (xpath);
3992 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3994 doc.InnerXml = value;
3997 XmlElement n = e.OwnerDocument.CreateElement (name);
3999 foreach (string a in attributes) {
4000 int eq = a.IndexOf ('=');
4001 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
4012 class DocumentationMember {
4013 public StringToStringMap MemberSignatures = new StringToStringMap ();
4014 public string ReturnType;
4015 public StringList Parameters;
4016 public StringList TypeParameters;
4017 public string MemberName;
4018 public string MemberType;
4020 public DocumentationMember (XmlReader reader)
4022 MemberName = reader.GetAttribute ("MemberName");
4023 int depth = reader.Depth;
4025 StringList p = new StringList ();
4026 StringList tp = new StringList ();
4028 if (reader.NodeType != XmlNodeType.Element)
4031 bool shouldUse = true;
4033 string apistyle = reader.GetAttribute ("apistyle");
4034 shouldUse = string.IsNullOrWhiteSpace(apistyle) || apistyle == "classic"; // only use this tag if it's an 'classic' style node
4036 catch (Exception ex) {}
4037 switch (reader.Name) {
4038 case "MemberSignature":
4040 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
4044 MemberType = reader.ReadElementString ();
4047 if (reader.Depth == depth + 2 && shouldUse)
4048 ReturnType = reader.ReadElementString ();
4051 if (reader.Depth == depth + 2 && shouldUse)
4052 p.Add (reader.GetAttribute ("Type"));
4054 case "TypeParameter":
4055 if (reader.Depth == depth + 2 && shouldUse)
4056 tp.Add (reader.GetAttribute ("Name"));
4059 if (reader.Depth == depth + 1)
4063 } while (go && reader.Read () && reader.Depth >= depth);
4068 TypeParameters = tp;
4070 DiscernTypeParameters ();
4074 public DocumentationMember (XmlNode node)
4076 MemberName = node.Attributes ["MemberName"].Value;
4077 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
4078 XmlAttribute l = n.Attributes ["Language"];
4079 XmlAttribute v = n.Attributes ["Value"];
4080 XmlAttribute apistyle = n.Attributes ["apistyle"];
4081 bool shouldUse = apistyle == null || apistyle.Value == "classic";
4082 if (l != null && v != null && shouldUse)
4083 MemberSignatures [l.Value] = v.Value;
4085 MemberType = node.SelectSingleNode ("MemberType").InnerText;
4086 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType[not(@apistyle) or @apistyle='classic']");
4088 ReturnType = rt.InnerText;
4089 XmlNodeList p = node.SelectNodes ("Parameters/Parameter[not(@apistyle) or @apistyle='classic']");
4091 Parameters = new StringList (p.Count);
4092 for (int i = 0; i < p.Count; ++i)
4093 Parameters.Add (p [i].Attributes ["Type"].Value);
4095 XmlNodeList tp = node.SelectNodes ("TypeParameters/TypeParameter[not(@apistyle) or @apistyle='classic']");
4097 TypeParameters = new StringList (tp.Count);
4098 for (int i = 0; i < tp.Count; ++i)
4099 TypeParameters.Add (tp [i].Attributes ["Name"].Value);
4102 DiscernTypeParameters ();
4106 void DiscernTypeParameters ()
4108 // see if we can discern the param list from the name
4109 if (MemberName.Contains ("<") && MemberName.EndsWith (">")) {
4110 var starti = MemberName.IndexOf ("<") + 1;
4111 var endi = MemberName.LastIndexOf (">");
4112 var paramlist = MemberName.Substring (starti, endi - starti);
4113 var tparams = paramlist.Split (new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
4114 TypeParameters = new StringList (tparams);
4119 public class DynamicParserContext {
4120 public ReadOnlyCollection<bool> TransformFlags;
4121 public int TransformIndex;
4123 public DynamicParserContext (ICustomAttributeProvider provider)
4126 if (provider.HasCustomAttributes &&
4127 (da = (provider.CustomAttributes.Cast<CustomAttribute>()
4128 .SingleOrDefault (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))) != null) {
4129 CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
4130 ? new CustomAttributeArgument [0]
4131 : (CustomAttributeArgument[]) da.ConstructorArguments [0].Value;
4133 TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool) t.Value).ToArray());
4138 public enum MemberFormatterState {
4140 WithinGenericTypeParameters,
4143 public abstract class MemberFormatter {
4145 public virtual string Language {
4149 public string GetName (MemberReference member)
4151 return GetName (member, null);
4154 public virtual string GetName (MemberReference member, DynamicParserContext context)
4156 TypeReference type = member as TypeReference;
4158 return GetTypeName (type, context);
4159 MethodReference method = member as MethodReference;
4160 if (method != null && method.Name == ".ctor") // method.IsConstructor
4161 return GetConstructorName (method);
4163 return GetMethodName (method);
4164 PropertyReference prop = member as PropertyReference;
4166 return GetPropertyName (prop);
4167 FieldReference field = member as FieldReference;
4169 return GetFieldName (field);
4170 EventReference e = member as EventReference;
4172 return GetEventName (e);
4173 throw new NotSupportedException ("Can't handle: " +
4174 (member == null ? "null" : member.GetType().ToString()));
4177 protected virtual string GetTypeName (TypeReference type)
4179 return GetTypeName (type, null);
4182 protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
4185 throw new ArgumentNullException ("type");
4186 return _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
4189 protected virtual char[] ArrayDelimeters {
4190 get {return new char[]{'[', ']'};}
4193 protected virtual MemberFormatterState MemberFormatterState { get; set; }
4195 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4197 if (type is ArrayType) {
4198 TypeSpecification spec = type as TypeSpecification;
4199 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
4200 return AppendArrayModifiers (buf, (ArrayType) type);
4202 if (type is ByReferenceType) {
4203 return AppendRefTypeName (buf, type, context);
4205 if (type is PointerType) {
4206 return AppendPointerTypeName (buf, type, context);
4208 if (type is GenericParameter) {
4209 return AppendTypeName (buf, type, context);
4211 AppendNamespace (buf, type);
4212 GenericInstanceType genInst = type as GenericInstanceType;
4213 if (type.GenericParameters.Count == 0 &&
4214 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
4215 return AppendFullTypeName (buf, type, context);
4217 return AppendGenericType (buf, type, context);
4220 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4222 string ns = DocUtils.GetNamespace (type);
4223 if (ns != null && ns.Length > 0)
4224 buf.Append (ns).Append ('.');
4228 protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4230 if (type.DeclaringType != null)
4231 AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
4232 return AppendTypeName (buf, type, context);
4235 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4237 if (context != null)
4238 context.TransformIndex++;
4239 return AppendTypeName (buf, type.Name);
4242 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
4244 int n = typename.IndexOf ("`");
4246 return buf.Append (typename.Substring (0, n));
4247 return buf.Append (typename);
4250 protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
4252 buf.Append (ArrayDelimeters [0]);
4253 int rank = array.Rank;
4255 buf.Append (new string (',', rank-1));
4256 return buf.Append (ArrayDelimeters [1]);
4259 protected virtual string RefTypeModifier {
4263 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4265 TypeSpecification spec = type as TypeSpecification;
4266 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4267 .Append (RefTypeModifier);
4270 protected virtual string PointerModifier {
4274 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4276 TypeSpecification spec = type as TypeSpecification;
4277 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4278 .Append (PointerModifier);
4281 protected virtual char[] GenericTypeContainer {
4282 get {return new char[]{'<', '>'};}
4285 protected virtual char NestedTypeSeparator {
4289 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4291 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4292 type is GenericInstanceType ? type.GetElementType () : type);
4293 List<TypeReference> genArgs = GetGenericArguments (type);
4296 bool insertNested = false;
4297 foreach (var decl in decls) {
4298 TypeReference declDef = decl.Resolve () ?? decl;
4300 buf.Append (NestedTypeSeparator);
4302 insertNested = true;
4303 AppendTypeName (buf, declDef, context);
4304 int ac = DocUtils.GetGenericArgumentCount (declDef);
4308 buf.Append (GenericTypeContainer [0]);
4309 var origState = MemberFormatterState;
4310 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4311 _AppendTypeName (buf, genArgs [argIdx++], context);
4312 for (int i = 1; i < c; ++i) {
4313 _AppendTypeName (buf.Append (","), genArgs [argIdx++], context);
4315 MemberFormatterState = origState;
4316 buf.Append (GenericTypeContainer [1]);
4322 protected List<TypeReference> GetGenericArguments (TypeReference type)
4324 var args = new List<TypeReference> ();
4325 GenericInstanceType inst = type as GenericInstanceType;
4327 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
4329 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
4333 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4338 protected virtual string GetConstructorName (MethodReference constructor)
4340 return constructor.Name;
4343 protected virtual string GetMethodName (MethodReference method)
4348 protected virtual string GetPropertyName (PropertyReference property)
4350 return property.Name;
4353 protected virtual string GetFieldName (FieldReference field)
4358 protected virtual string GetEventName (EventReference e)
4363 public virtual string GetDeclaration (MemberReference member)
4366 throw new ArgumentNullException ("member");
4367 TypeDefinition type = member as TypeDefinition;
4369 return GetTypeDeclaration (type);
4370 MethodDefinition method = member as MethodDefinition;
4371 if (method != null && method.IsConstructor)
4372 return GetConstructorDeclaration (method);
4374 return GetMethodDeclaration (method);
4375 PropertyDefinition prop = member as PropertyDefinition;
4377 return GetPropertyDeclaration (prop);
4378 FieldDefinition field = member as FieldDefinition;
4380 return GetFieldDeclaration (field);
4381 EventDefinition e = member as EventDefinition;
4383 return GetEventDeclaration (e);
4384 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
4387 protected virtual string GetTypeDeclaration (TypeDefinition type)
4390 throw new ArgumentNullException ("type");
4391 StringBuilder buf = new StringBuilder (type.Name.Length);
4392 _AppendTypeName (buf, type, null);
4393 AppendGenericTypeConstraints (buf, type);
4394 return buf.ToString ();
4397 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
4399 return GetConstructorName (constructor);
4402 protected virtual string GetMethodDeclaration (MethodDefinition method)
4404 if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
4405 ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
4408 // Special signature for destructors.
4409 if (method.Name == "Finalize" && method.Parameters.Count == 0)
4410 return GetFinalizerName (method);
4412 StringBuilder buf = new StringBuilder ();
4414 AppendVisibility (buf, method);
4415 if (buf.Length == 0 &&
4416 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
4419 AppendModifiers (buf, method);
4421 if (buf.Length != 0)
4423 buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
4425 AppendMethodName (buf, method);
4426 AppendGenericMethod (buf, method).Append (" ");
4427 AppendParameters (buf, method, method.Parameters);
4428 AppendGenericMethodConstraints (buf, method);
4429 return buf.ToString ();
4432 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4434 return buf.Append (method.Name);
4437 protected virtual string GetFinalizerName (MethodDefinition method)
4442 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4447 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4452 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4457 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4462 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4467 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
4469 return GetPropertyName (property);
4472 protected virtual string GetFieldDeclaration (FieldDefinition field)
4474 return GetFieldName (field);
4477 protected virtual string GetEventDeclaration (EventDefinition e)
4479 return GetEventName (e);
4483 class ILFullMemberFormatter : MemberFormatter {
4485 public override string Language {
4486 get {return "ILAsm";}
4489 protected override char NestedTypeSeparator {
4495 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4497 if (GetBuiltinType (type.FullName) != null)
4499 string ns = DocUtils.GetNamespace (type);
4500 if (ns != null && ns.Length > 0) {
4501 if (type.IsValueType)
4502 buf.Append ("valuetype ");
4504 buf.Append ("class ");
4505 buf.Append (ns).Append ('.');
4510 protected static string GetBuiltinType (string t)
4513 case "System.Byte": return "unsigned int8";
4514 case "System.SByte": return "int8";
4515 case "System.Int16": return "int16";
4516 case "System.Int32": return "int32";
4517 case "System.Int64": return "int64";
4518 case "System.IntPtr": return "native int";
4520 case "System.UInt16": return "unsigned int16";
4521 case "System.UInt32": return "unsigned int32";
4522 case "System.UInt64": return "unsigned int64";
4523 case "System.UIntPtr": return "native unsigned int";
4525 case "System.Single": return "float32";
4526 case "System.Double": return "float64";
4527 case "System.Boolean": return "bool";
4528 case "System.Char": return "char";
4529 case "System.Void": return "void";
4530 case "System.String": return "string";
4531 case "System.Object": return "object";
4536 protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
4538 return buf.Append (typename);
4541 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4543 if (type is GenericParameter) {
4544 AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
4548 string s = GetBuiltinType (type.FullName);
4550 return buf.Append (s);
4552 return base.AppendTypeName (buf, type, context);
4555 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
4557 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
4558 return buf.Append (type.Owner is TypeReference ? "!" : "!!");
4560 GenericParameterAttributes attrs = type.Attributes;
4561 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
4562 buf.Append ("class ");
4563 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
4564 buf.Append ("struct ");
4565 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
4566 buf.Append (".ctor ");
4567 IList<TypeReference> constraints = type.Constraints;
4568 MemberFormatterState = 0;
4569 if (constraints.Count > 0) {
4570 var full = new ILFullMemberFormatter ();
4571 buf.Append ("(").Append (full.GetName (constraints [0]));
4572 for (int i = 1; i < constraints.Count; ++i) {
4573 buf.Append (", ").Append (full.GetName (constraints [i]));
4577 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4579 if ((attrs & GenericParameterAttributes.Covariant) != 0)
4581 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
4586 protected override string GetTypeDeclaration (TypeDefinition type)
4588 string visibility = GetTypeVisibility (type.Attributes);
4589 if (visibility == null)
4592 StringBuilder buf = new StringBuilder ();
4594 buf.Append (".class ");
4596 buf.Append ("nested ");
4597 buf.Append (visibility).Append (" ");
4598 if (type.IsInterface)
4599 buf.Append ("interface ");
4600 if (type.IsSequentialLayout)
4601 buf.Append ("sequential ");
4602 if (type.IsAutoLayout)
4603 buf.Append ("auto ");
4604 if (type.IsAnsiClass)
4605 buf.Append ("ansi ");
4606 if (type.IsAbstract)
4607 buf.Append ("abstract ");
4608 if (type.IsSerializable)
4609 buf.Append ("serializable ");
4611 buf.Append ("sealed ");
4612 if (type.IsBeforeFieldInit)
4613 buf.Append ("beforefieldinit ");
4614 var state = MemberFormatterState;
4615 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4616 buf.Append (GetName (type));
4617 MemberFormatterState = state;
4618 var full = new ILFullMemberFormatter ();
4619 if (type.BaseType != null) {
4620 buf.Append (" extends ");
4621 if (type.BaseType.FullName == "System.Object")
4622 buf.Append ("System.Object");
4624 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
4627 foreach (var name in type.Interfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()))
4628 .Select (i => full.GetName (i))
4629 .OrderBy (n => n)) {
4631 buf.Append (" implements ");
4640 return buf.ToString ();
4643 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4645 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4646 type is GenericInstanceType ? type.GetElementType () : type);
4648 foreach (var decl in decls) {
4649 TypeReference declDef = decl.Resolve () ?? decl;
4651 buf.Append (NestedTypeSeparator);
4654 AppendTypeName (buf, declDef, context);
4658 foreach (TypeReference arg in GetGenericArguments (type)) {
4662 _AppendTypeName (buf, arg, context);
4668 static string GetTypeVisibility (TypeAttributes ta)
4670 switch (ta & TypeAttributes.VisibilityMask) {
4671 case TypeAttributes.Public:
4672 case TypeAttributes.NestedPublic:
4675 case TypeAttributes.NestedFamily:
4676 case TypeAttributes.NestedFamORAssem:
4684 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4686 return GetMethodDeclaration (constructor);
4689 protected override string GetMethodDeclaration (MethodDefinition method)
4691 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
4694 var buf = new StringBuilder ();
4695 buf.Append (".method ");
4696 AppendVisibility (buf, method);
4697 if (method.IsStatic)
4698 buf.Append ("static ");
4699 if (method.IsHideBySig)
4700 buf.Append ("hidebysig ");
4701 if (method.IsPInvokeImpl) {
4702 var info = method.PInvokeInfo;
4703 buf.Append ("pinvokeimpl (\"")
4704 .Append (info.Module.Name)
4705 .Append ("\" as \"")
4706 .Append (info.EntryPoint)
4708 if (info.IsCharSetAuto)
4709 buf.Append (" auto");
4710 if (info.IsCharSetUnicode)
4711 buf.Append (" unicode");
4712 if (info.IsCharSetAnsi)
4713 buf.Append (" ansi");
4714 if (info.IsCallConvCdecl)
4715 buf.Append (" cdecl");
4716 if (info.IsCallConvStdCall)
4717 buf.Append (" stdcall");
4718 if (info.IsCallConvWinapi)
4719 buf.Append (" winapi");
4720 if (info.IsCallConvThiscall)
4721 buf.Append (" thiscall");
4722 if (info.SupportsLastError)
4723 buf.Append (" lasterr");
4726 if (method.IsSpecialName)
4727 buf.Append ("specialname ");
4728 if (method.IsRuntimeSpecialName)
4729 buf.Append ("rtspecialname ");
4730 if (method.IsNewSlot)
4731 buf.Append ("newslot ");
4732 if (method.IsVirtual)
4733 buf.Append ("virtual ");
4734 if (!method.IsStatic)
4735 buf.Append ("instance ");
4736 _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
4738 .Append (method.Name);
4739 if (method.IsGenericMethod ()) {
4740 var state = MemberFormatterState;
4741 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4742 IList<GenericParameter> args = method.GenericParameters;
4743 if (args.Count > 0) {
4745 _AppendTypeName (buf, args [0], null);
4746 for (int i = 1; i < args.Count; ++i)
4747 _AppendTypeName (buf.Append (", "), args [i], null);
4750 MemberFormatterState = state;
4755 for (int i = 0; i < method.Parameters.Count; ++i) {
4759 _AppendTypeName (buf, method.Parameters [i].ParameterType, new DynamicParserContext (method.Parameters [i]));
4761 buf.Append (method.Parameters [i].Name);
4765 buf.Append (" cil");
4766 if (method.IsRuntime)
4767 buf.Append (" runtime");
4768 if (method.IsManaged)
4769 buf.Append (" managed");
4771 return buf.ToString ();
4774 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4776 if (DocUtils.IsExplicitlyImplemented (method)) {
4777 TypeReference iface;
4778 MethodReference ifaceMethod;
4779 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4780 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4782 .Append (ifaceMethod.Name);
4784 return base.AppendMethodName (buf, method);
4787 protected override string RefTypeModifier {
4791 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4793 if (method.IsPublic)
4794 return buf.Append ("public ");
4795 if (method.IsFamilyAndAssembly)
4796 return buf.Append ("familyandassembly");
4797 if (method.IsFamilyOrAssembly)
4798 return buf.Append ("familyorassembly");
4799 if (method.IsFamily)
4800 return buf.Append ("family");
4804 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4806 string modifiers = String.Empty;
4807 if (method.IsStatic) modifiers += " static";
4808 if (method.IsVirtual && !method.IsAbstract) {
4809 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4810 else modifiers += " override";
4812 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4813 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4814 if (method.IsFinal) modifiers += " sealed";
4815 if (modifiers == " virtual sealed") modifiers = "";
4817 return buf.Append (modifiers);
4820 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4822 if (method.IsGenericMethod ()) {
4823 IList<GenericParameter> args = method.GenericParameters;
4824 if (args.Count > 0) {
4826 buf.Append (args [0].Name);
4827 for (int i = 1; i < args.Count; ++i)
4828 buf.Append (",").Append (args [i].Name);
4835 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4837 return AppendParameters (buf, method, parameters, '(', ')');
4840 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4844 if (parameters.Count > 0) {
4845 if (DocUtils.IsExtensionMethod (method))
4846 buf.Append ("this ");
4847 AppendParameter (buf, parameters [0]);
4848 for (int i = 1; i < parameters.Count; ++i) {
4850 AppendParameter (buf, parameters [i]);
4854 return buf.Append (end);
4857 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4859 if (parameter.ParameterType is ByReferenceType) {
4860 if (parameter.IsOut)
4861 buf.Append ("out ");
4863 buf.Append ("ref ");
4865 buf.Append (GetName (parameter.ParameterType)).Append (" ");
4866 return buf.Append (parameter.Name);
4869 protected override string GetPropertyDeclaration (PropertyDefinition property)
4871 MethodDefinition gm = null, sm = null;
4873 string get_visible = null;
4874 if ((gm = property.GetMethod) != null &&
4875 (DocUtils.IsExplicitlyImplemented (gm) ||
4876 (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
4877 get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
4878 string set_visible = null;
4879 if ((sm = property.SetMethod) != null &&
4880 (DocUtils.IsExplicitlyImplemented (sm) ||
4881 (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
4882 set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
4884 if ((set_visible == null) && (get_visible == null))
4887 StringBuilder buf = new StringBuilder ()
4888 .Append (".property ");
4889 if (!(gm ?? sm).IsStatic)
4890 buf.Append ("instance ");
4891 _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
4892 buf.Append (' ').Append (property.Name);
4893 if (!property.HasParameters || property.Parameters.Count == 0)
4894 return buf.ToString ();
4898 foreach (ParameterDefinition p in property.Parameters) {
4902 _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
4906 return buf.ToString ();
4909 protected override string GetFieldDeclaration (FieldDefinition field)
4911 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4912 if (declType.IsEnum && field.Name == "value__")
4913 return null; // This member of enums aren't documented.
4915 StringBuilder buf = new StringBuilder ();
4916 AppendFieldVisibility (buf, field);
4917 if (buf.Length == 0)
4920 buf.Insert (0, ".field ");
4923 buf.Append ("static ");
4924 if (field.IsInitOnly)
4925 buf.Append ("initonly ");
4926 if (field.IsLiteral)
4927 buf.Append ("literal ");
4928 _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
4929 buf.Append (' ').Append (field.Name);
4930 AppendFieldValue (buf, field);
4932 return buf.ToString ();
4935 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4938 return buf.Append ("public ");
4939 if (field.IsFamilyAndAssembly)
4940 return buf.Append ("familyandassembly ");
4941 if (field.IsFamilyOrAssembly)
4942 return buf.Append ("familyorassembly ");
4944 return buf.Append ("family ");
4948 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4950 // enums have a value__ field, which we ignore
4951 if (field.DeclaringType.IsGenericType ())
4953 if (field.HasConstant && field.IsLiteral) {
4956 val = field.Constant;
4961 buf.Append (" = ").Append ("null");
4962 else if (val is Enum)
4964 .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4966 .Append (val.ToString ())
4968 else if (val is IFormattable) {
4969 string value = ((IFormattable)val).ToString();
4972 buf.Append ("\"" + value + "\"");
4974 buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4983 protected override string GetEventDeclaration (EventDefinition e)
4985 StringBuilder buf = new StringBuilder ();
4986 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4991 buf.Append (".event ")
4992 .Append (GetName (e.EventType))
4996 return buf.ToString ();
5000 class ILMemberFormatter : ILFullMemberFormatter {
5001 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5007 class ILNativeTypeMemberFormatter : ILFullMemberFormatter {
5008 protected static string _GetBuiltinType (string t)
5010 //string moddedType = base.GetBuiltinType (t);
5012 //return moddedType;
5016 class CSharpNativeTypeMemberFormatter : CSharpFullMemberFormatter {
5017 protected override string GetCSharpType (string t) {
5018 string moddedType = base.GetCSharpType (t);
5020 switch (moddedType) {
5021 case "int": return "nint";
5026 case "System.Drawing.SizeF":
5027 return "CoreGraphics.CGSize";
5028 case "System.Drawing.PointF":
5029 return "CoreGraphics.CGPoint";
5030 case "System.Drawing.RectangleF":
5031 return "CoreGraphics.CGPoint";
5037 class CSharpFullMemberFormatter : MemberFormatter {
5039 public override string Language {
5043 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5046 string ns = DocUtils.GetNamespace (type);
5047 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
5048 buf.Append (ns).Append ('.');
5052 protected virtual string GetCSharpType (string t)
5055 case "System.Byte": return "byte";
5056 case "System.SByte": return "sbyte";
5057 case "System.Int16": return "short";
5058 case "System.Int32": return "int";
5059 case "System.Int64": return "long";
5061 case "System.UInt16": return "ushort";
5062 case "System.UInt32": return "uint";
5063 case "System.UInt64": return "ulong";
5065 case "System.Single": return "float";
5066 case "System.Double": return "double";
5067 case "System.Decimal": return "decimal";
5068 case "System.Boolean": return "bool";
5069 case "System.Char": return "char";
5070 case "System.Void": return "void";
5071 case "System.String": return "string";
5072 case "System.Object": return "object";
5077 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
5079 if (context != null && context.TransformFlags != null &&
5080 (context.TransformFlags.Count == 0 || context.TransformFlags [context.TransformIndex])) {
5081 context.TransformIndex++;
5082 return buf.Append ("dynamic");
5085 if (type is GenericParameter)
5086 return AppendGenericParameterConstraints (buf, (GenericParameter) type, context).Append (type.Name);
5087 string t = type.FullName;
5088 if (!t.StartsWith ("System.")) {
5089 return base.AppendTypeName (buf, type, context);
5092 string s = GetCSharpType (t);
5094 if (context != null)
5095 context.TransformIndex++;
5096 return buf.Append (s);
5099 return base.AppendTypeName (buf, type, context);
5102 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
5104 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
5106 GenericParameterAttributes attrs = type.Attributes;
5107 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
5108 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
5112 buf.Append ("out ");
5116 protected override string GetTypeDeclaration (TypeDefinition type)
5118 string visibility = GetTypeVisibility (type.Attributes);
5119 if (visibility == null)
5122 StringBuilder buf = new StringBuilder ();
5124 buf.Append (visibility);
5127 MemberFormatter full = new CSharpFullMemberFormatter ();
5129 if (DocUtils.IsDelegate (type)) {
5130 buf.Append("delegate ");
5131 MethodDefinition invoke = type.GetMethod ("Invoke");
5132 buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
5133 buf.Append (GetName (type));
5134 AppendParameters (buf, invoke, invoke.Parameters);
5135 AppendGenericTypeConstraints (buf, type);
5138 return buf.ToString();
5141 if (type.IsAbstract && !type.IsInterface)
5142 buf.Append("abstract ");
5143 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
5144 buf.Append("sealed ");
5145 buf.Replace ("abstract sealed", "static");
5147 buf.Append (GetTypeKind (type));
5149 buf.Append (GetCSharpType (type.FullName) == null
5154 TypeReference basetype = type.BaseType;
5155 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
5158 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
5159 .Select (iface => full.GetName (iface))
5163 if (basetype != null || interface_names.Count > 0)
5166 if (basetype != null) {
5167 buf.Append (full.GetName (basetype));
5168 if (interface_names.Count > 0)
5172 for (int i = 0; i < interface_names.Count; i++){
5175 buf.Append (interface_names [i]);
5177 AppendGenericTypeConstraints (buf, type);
5180 return buf.ToString ();
5183 static string GetTypeKind (TypeDefinition t)
5189 if (t.IsClass || t.FullName == "System.Enum")
5193 throw new ArgumentException(t.FullName);
5196 static string GetTypeVisibility (TypeAttributes ta)
5198 switch (ta & TypeAttributes.VisibilityMask) {
5199 case TypeAttributes.Public:
5200 case TypeAttributes.NestedPublic:
5203 case TypeAttributes.NestedFamily:
5204 case TypeAttributes.NestedFamORAssem:
5212 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
5214 if (type.GenericParameters.Count == 0)
5216 return AppendConstraints (buf, type.GenericParameters);
5219 private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
5221 foreach (GenericParameter genArg in genArgs) {
5222 GenericParameterAttributes attrs = genArg.Attributes;
5223 IList<TypeReference> constraints = genArg.Constraints;
5224 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
5227 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
5228 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
5229 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
5232 if (!isref && !isvt && !isnew && constraints.Count == 0)
5234 buf.Append (" where ").Append (genArg.Name).Append (" : ");
5236 buf.Append ("class");
5240 buf.Append ("struct");
5243 if (constraints.Count > 0 && !isvt) {
5246 buf.Append (GetTypeName (constraints [0]));
5247 for (int i = 1; i < constraints.Count; ++i)
5248 buf.Append (", ").Append (GetTypeName (constraints [i]));
5250 if (isnew && !isvt) {
5253 buf.Append ("new()");
5259 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5261 StringBuilder buf = new StringBuilder ();
5262 AppendVisibility (buf, constructor);
5263 if (buf.Length == 0)
5267 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
5268 AppendParameters (buf, constructor, constructor.Parameters);
5271 return buf.ToString ();
5274 protected override string GetMethodDeclaration (MethodDefinition method)
5276 string decl = base.GetMethodDeclaration (method);
5282 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
5284 if (DocUtils.IsExplicitlyImplemented (method)) {
5285 TypeReference iface;
5286 MethodReference ifaceMethod;
5287 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5288 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
5290 .Append (ifaceMethod.Name);
5292 return base.AppendMethodName (buf, method);
5295 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
5297 if (method.GenericParameters.Count == 0)
5299 return AppendConstraints (buf, method.GenericParameters);
5302 protected override string RefTypeModifier {
5306 protected override string GetFinalizerName (MethodDefinition method)
5308 return "~" + method.DeclaringType.Name + " ()";
5311 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
5315 if (method.IsPublic)
5316 return buf.Append ("public");
5317 if (method.IsFamily || method.IsFamilyOrAssembly)
5318 return buf.Append ("protected");
5322 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
5324 string modifiers = String.Empty;
5325 if (method.IsStatic) modifiers += " static";
5326 if (method.IsVirtual && !method.IsAbstract) {
5327 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
5328 else modifiers += " override";
5330 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
5331 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
5332 if (method.IsFinal) modifiers += " sealed";
5333 if (modifiers == " virtual sealed") modifiers = "";
5335 return buf.Append (modifiers);
5338 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
5340 if (method.IsGenericMethod ()) {
5341 IList<GenericParameter> args = method.GenericParameters;
5342 if (args.Count > 0) {
5344 buf.Append (args [0].Name);
5345 for (int i = 1; i < args.Count; ++i)
5346 buf.Append (",").Append (args [i].Name);
5353 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
5355 return AppendParameters (buf, method, parameters, '(', ')');
5358 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
5362 if (parameters.Count > 0) {
5363 if (DocUtils.IsExtensionMethod (method))
5364 buf.Append ("this ");
5365 AppendParameter (buf, parameters [0]);
5366 for (int i = 1; i < parameters.Count; ++i) {
5368 AppendParameter (buf, parameters [i]);
5372 return buf.Append (end);
5375 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
5377 if (parameter.ParameterType is ByReferenceType) {
5378 if (parameter.IsOut)
5379 buf.Append ("out ");
5381 buf.Append ("ref ");
5383 buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
5384 buf.Append (parameter.Name);
5385 if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant) {
5386 buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
5391 protected override string GetPropertyDeclaration (PropertyDefinition property)
5393 MethodDefinition method;
5395 string get_visible = null;
5396 if ((method = property.GetMethod) != null &&
5397 (DocUtils.IsExplicitlyImplemented (method) ||
5398 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5399 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5400 string set_visible = null;
5401 if ((method = property.SetMethod) != null &&
5402 (DocUtils.IsExplicitlyImplemented (method) ||
5403 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5404 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5406 if ((set_visible == null) && (get_visible == null))
5410 StringBuilder buf = new StringBuilder ();
5411 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
5412 buf.Append (visibility = get_visible);
5413 else if (set_visible != null && get_visible == null)
5414 buf.Append (visibility = set_visible);
5416 buf.Append (visibility = "public");
5418 // Pick an accessor to use for static/virtual/override/etc. checks.
5419 method = property.SetMethod;
5421 method = property.GetMethod;
5423 string modifiers = String.Empty;
5424 if (method.IsStatic) modifiers += " static";
5425 if (method.IsVirtual && !method.IsAbstract) {
5426 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
5427 modifiers += " virtual";
5429 modifiers += " override";
5431 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
5432 if (method.IsAbstract && !declDef.IsInterface)
5433 modifiers += " abstract";
5435 modifiers += " sealed";
5436 if (modifiers == " virtual sealed")
5438 buf.Append (modifiers).Append (' ');
5440 buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
5442 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
5443 string name = property.Name;
5444 foreach (MemberReference mi in defs) {
5445 if (mi == property) {
5450 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
5452 if (property.Parameters.Count != 0) {
5453 AppendParameters (buf, method, property.Parameters, '[', ']');
5457 if (get_visible != null) {
5458 if (get_visible != visibility)
5459 buf.Append (' ').Append (get_visible);
5460 buf.Append (" get;");
5462 if (set_visible != null) {
5463 if (set_visible != visibility)
5464 buf.Append (' ').Append (set_visible);
5465 buf.Append (" set;");
5469 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
5472 protected override string GetFieldDeclaration (FieldDefinition field)
5474 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
5475 if (declType.IsEnum && field.Name == "value__")
5476 return null; // This member of enums aren't documented.
5478 StringBuilder buf = new StringBuilder ();
5479 AppendFieldVisibility (buf, field);
5480 if (buf.Length == 0)
5483 if (declType.IsEnum)
5486 if (field.IsStatic && !field.IsLiteral)
5487 buf.Append (" static");
5488 if (field.IsInitOnly)
5489 buf.Append (" readonly");
5490 if (field.IsLiteral)
5491 buf.Append (" const");
5493 buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
5494 buf.Append (field.Name);
5495 AppendFieldValue (buf, field);
5498 return buf.ToString ();
5501 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
5504 return buf.Append ("public");
5505 if (field.IsFamily || field.IsFamilyOrAssembly)
5506 return buf.Append ("protected");
5510 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
5512 // enums have a value__ field, which we ignore
5513 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
5514 field.DeclaringType.IsGenericType ())
5516 if (field.HasConstant && field.IsLiteral) {
5519 val = field.Constant;
5524 buf.Append (" = ").Append ("null");
5525 else if (val is Enum)
5526 buf.Append (" = ").Append (val.ToString ());
5527 else if (val is IFormattable) {
5528 string value = ((IFormattable)val).ToString();
5530 value = "\"" + value + "\"";
5531 buf.Append (" = ").Append (value);
5537 protected override string GetEventDeclaration (EventDefinition e)
5539 StringBuilder buf = new StringBuilder ();
5540 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
5544 AppendModifiers (buf, e.AddMethod);
5546 buf.Append (" event ");
5547 buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters [0]))).Append (' ');
5548 buf.Append (e.Name).Append (';');
5550 return buf.ToString ();
5554 class CSharpMemberFormatter : CSharpFullMemberFormatter {
5555 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5561 class DocTypeFullMemberFormatter : MemberFormatter {
5562 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
5564 protected override char NestedTypeSeparator {
5569 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
5570 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5576 class SlashDocMemberFormatter : MemberFormatter {
5578 protected override char[] GenericTypeContainer {
5579 get {return new char[]{'{', '}'};}
5582 private bool AddTypeCount = true;
5584 private TypeReference genDeclType;
5585 private MethodReference genDeclMethod;
5587 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
5589 if (type is GenericParameter) {
5591 if (genDeclType != null) {
5592 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
5593 for (int i = 0; i < genArgs.Count; ++i) {
5594 if (genArgs [i].Name == type.Name) {
5595 buf.Append ('`').Append (i);
5600 if (genDeclMethod != null) {
5601 IList<GenericParameter> genArgs = null;
5602 if (genDeclMethod.IsGenericMethod ()) {
5603 genArgs = genDeclMethod.GenericParameters;
5604 for (int i = 0; i < genArgs.Count; ++i) {
5605 if (genArgs [i].Name == type.Name) {
5606 buf.Append ("``").Append (i);
5612 if (genDeclType == null && genDeclMethod == null) {
5613 // Probably from within an explicitly implemented interface member,
5614 // where CSC uses parameter names instead of indices (why?), e.g.
5615 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
5616 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
5617 buf.Append (type.Name);
5619 if (buf.Length == l) {
5620 throw new Exception (string.Format (
5621 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
5622 type.Name, genDeclType, genDeclMethod));
5626 base.AppendTypeName (buf, type, context);
5628 int numArgs = type.GenericParameters.Count;
5629 if (type.DeclaringType != null)
5630 numArgs -= type.GenericParameters.Count;
5632 buf.Append ('`').Append (numArgs);
5639 protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
5641 buf.Append (ArrayDelimeters [0]);
5642 int rank = array.Rank;
5645 for (int i = 1; i < rank; ++i) {
5649 return buf.Append (ArrayDelimeters [1]);
5652 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5655 base.AppendGenericType (buf, type, context);
5657 AppendType (buf, type, context);
5661 private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5663 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
5664 bool insertNested = false;
5665 int prevParamCount = 0;
5666 foreach (var decl in decls) {
5668 buf.Append (NestedTypeSeparator);
5669 insertNested = true;
5670 base.AppendTypeName (buf, decl, context);
5671 int argCount = DocUtils.GetGenericArgumentCount (decl);
5672 int numArgs = argCount - prevParamCount;
5673 prevParamCount = argCount;
5675 buf.Append ('`').Append (numArgs);
5680 public override string GetDeclaration (MemberReference member)
5682 TypeReference r = member as TypeReference;
5684 return "T:" + GetTypeName (r);
5686 return base.GetDeclaration (member);
5689 protected override string GetConstructorName (MethodReference constructor)
5691 return GetMethodDefinitionName (constructor, "#ctor");
5694 protected override string GetMethodName (MethodReference method)
5697 MethodDefinition methodDef = method as MethodDefinition;
5698 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
5701 TypeReference iface;
5702 MethodReference ifaceMethod;
5703 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
5704 AddTypeCount = false;
5705 name = GetTypeName (iface) + "." + ifaceMethod.Name;
5706 AddTypeCount = true;
5708 return GetMethodDefinitionName (method, name);
5711 private string GetMethodDefinitionName (MethodReference method, string name)
5713 StringBuilder buf = new StringBuilder ();
5714 buf.Append (GetTypeName (method.DeclaringType));
5716 buf.Append (name.Replace (".", "#"));
5717 if (method.IsGenericMethod ()) {
5718 IList<GenericParameter> genArgs = method.GenericParameters;
5719 if (genArgs.Count > 0)
5720 buf.Append ("``").Append (genArgs.Count);
5722 IList<ParameterDefinition> parameters = method.Parameters;
5724 genDeclType = method.DeclaringType;
5725 genDeclMethod = method;
5726 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
5730 genDeclMethod = null;
5732 return buf.ToString ();
5735 private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
5737 if (parameters.Count == 0)
5742 AppendParameter (buf, genArgs, parameters [0]);
5743 for (int i = 1; i < parameters.Count; ++i) {
5745 AppendParameter (buf, genArgs, parameters [i]);
5748 return buf.Append (')');
5751 private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
5753 AddTypeCount = false;
5754 buf.Append (GetTypeName (parameter.ParameterType));
5755 AddTypeCount = true;
5759 protected override string GetPropertyName (PropertyReference property)
5763 PropertyDefinition propertyDef = property as PropertyDefinition;
5764 MethodDefinition method = null;
5765 if (propertyDef != null)
5766 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
5767 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
5768 name = property.Name;
5770 TypeReference iface;
5771 MethodReference ifaceMethod;
5772 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5773 AddTypeCount = false;
5774 name = string.Join ("#", new string[]{
5775 GetTypeName (iface).Replace (".", "#"),
5776 DocUtils.GetMember (property.Name)
5778 AddTypeCount = true;
5781 StringBuilder buf = new StringBuilder ();
5782 buf.Append (GetName (property.DeclaringType));
5785 IList<ParameterDefinition> parameters = property.Parameters;
5786 if (parameters.Count > 0) {
5787 genDeclType = property.DeclaringType;
5789 IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
5790 AppendParameter (buf, genArgs, parameters [0]);
5791 for (int i = 1; i < parameters.Count; ++i) {
5793 AppendParameter (buf, genArgs, parameters [i]);
5798 return buf.ToString ();
5801 protected override string GetFieldName (FieldReference field)
5803 return string.Format ("{0}.{1}",
5804 GetName (field.DeclaringType), field.Name);
5807 protected override string GetEventName (EventReference e)
5809 return string.Format ("{0}.{1}",
5810 GetName (e.DeclaringType), e.Name);
5813 protected override string GetTypeDeclaration (TypeDefinition type)
5815 string name = GetName (type);
5821 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5823 string name = GetName (constructor);
5829 protected override string GetMethodDeclaration (MethodDefinition method)
5831 string name = GetName (method);
5834 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
5835 genDeclType = method.DeclaringType;
5836 genDeclMethod = method;
5837 name += "~" + GetName (method.ReturnType);
5839 genDeclMethod = null;
5844 protected override string GetPropertyDeclaration (PropertyDefinition property)
5846 string name = GetName (property);
5852 protected override string GetFieldDeclaration (FieldDefinition field)
5854 string name = GetName (field);
5860 protected override string GetEventDeclaration (EventDefinition e)
5862 string name = GetName (e);
5869 class FileNameMemberFormatter : SlashDocMemberFormatter {
5870 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5875 protected override char NestedTypeSeparator {
5880 class ResolvedTypeInfo {
5881 TypeDefinition typeDef;
5883 public ResolvedTypeInfo (TypeReference value) {
5887 public TypeReference Reference { get; private set; }
5889 public TypeDefinition Definition {
5891 if (typeDef == null) {
5892 typeDef = Reference.Resolve ();
5899 /// <summary>Formats attribute values. Should return true if it is able to format the value.</summary>
5900 class AttributeValueFormatter {
5901 public virtual bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5903 TypeReference valueType = type.Reference;
5905 returnvalue = "null";
5908 if (valueType.FullName == "System.Type") {
5909 var vTypeRef = v as TypeReference;
5910 if (vTypeRef != null)
5911 returnvalue = "typeof(" + NativeTypeManager.GetTranslatedName (vTypeRef) + ")"; // TODO: drop NS handling
5913 returnvalue = "typeof(" + v.ToString () + ")";
5917 if (valueType.FullName == "System.String") {
5918 returnvalue = "\"" + v.ToString () + "\"";
5921 if (valueType.FullName == "System.Char") {
5922 returnvalue = "'" + v.ToString () + "'";
5926 returnvalue = (bool)v ? "true" : "false";
5930 TypeDefinition valueDef = type.Definition;
5931 if (valueDef == null || !valueDef.IsEnum) {
5932 returnvalue = v.ToString ();
5936 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5937 var values = MDocUpdater.GetEnumerationValues (valueDef);
5938 long c = MDocUpdater.ToInt64 (v);
5939 if (values.ContainsKey (c)) {
5940 returnvalue = typename + "." + values [c];
5949 /// <summary>The final value formatter in the pipeline ... if no other formatter formats the value,
5950 /// then this one will serve as the default implementation.</summary>
5951 class DefaultAttributeValueFormatter : AttributeValueFormatter {
5952 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5954 returnvalue = "(" + MDocUpdater.GetDocTypeFullName (type.Reference) + ") " + v.ToString ();
5959 /// <summary>Flags enum formatter that assumes powers of two values.</summary>
5960 /// <remarks>As described here: https://msdn.microsoft.com/en-us/library/vstudio/ms229062(v=vs.100).aspx</remarks>
5961 class StandardFlagsEnumFormatter : AttributeValueFormatter {
5962 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5964 TypeReference valueType = type.Reference;
5965 TypeDefinition valueDef = type.Definition;
5966 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
5968 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5969 var values = MDocUpdater.GetEnumerationValues (valueDef);
5970 long c = MDocUpdater.ToInt64 (v);
5971 returnvalue = string.Join (" | ",
5972 (from i in values.Keys
5973 where (c & i) == i && i != 0
5974 select typename + "." + values [i])
5975 .DefaultIfEmpty (c.ToString ()).ToArray ());
5985 /// <summary>A custom formatter for the ObjCRuntime.Platform enumeration.</summary>
5986 class ApplePlatformEnumFormatter : AttributeValueFormatter {
5987 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5989 TypeReference valueType = type.Reference;
5990 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5991 TypeDefinition valueDef = type.Definition;
5992 if (typename.Contains ("ObjCRuntime.Platform") && valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
5994 var values = MDocUpdater.GetEnumerationValues (valueDef);
5995 long c = MDocUpdater.ToInt64 (v);
5997 returnvalue = Format (c, values, typename);
6005 string Format (long c, IDictionary<long, string> values, string typename)
6007 int iosarch, iosmajor, iosminor, iossubminor;
6008 int macarch, macmajor, macminor, macsubminor;
6009 GetEncodingiOS (c, out iosarch, out iosmajor, out iosminor, out iossubminor);
6010 GetEncodingMac ((ulong)c, out macarch, out macmajor, out macminor, out macsubminor);
6012 if (iosmajor == 0 & iosminor == 0 && iossubminor == 0) {
6013 return FormatValues ("Mac", macarch, macmajor, macminor, macsubminor);
6016 if (macmajor == 0 & macminor == 0 && macsubminor == 0) {
6017 return FormatValues ("iOS", iosarch, iosmajor, iosminor, iossubminor);
6020 return string.Format ("(Platform){0}", c);
6023 string FormatValues (string plat, int arch, int major, int minor, int subminor)
6025 string archstring = "";
6034 return string.Format ("Platform.{4}_{0}_{1}{2} | Platform.{4}_Arch{3}",
6037 subminor == 0 ? "" : "_" + subminor.ToString (),
6043 void GetEncodingiOS (long entireLong, out int archindex, out int major, out int minor, out int subminor)
6045 long lowerBits = entireLong & 0xffffffff;
6046 int lowerBitsAsInt = (int) lowerBits;
6047 GetEncoding (lowerBitsAsInt, out archindex, out major, out minor, out subminor);
6050 void GetEncodingMac (ulong entireLong, out int archindex, out int major, out int minor, out int subminor)
6052 ulong higherBits = entireLong & 0xffffffff00000000;
6053 int higherBitsAsInt = (int) ((higherBits) >> 32);
6054 GetEncoding (higherBitsAsInt, out archindex, out major, out minor, out subminor);
6057 void GetEncoding (Int32 encodedBits, out int archindex, out int major, out int minor, out int subminor)
6059 // format is AAJJNNSS
6060 archindex = (int)((encodedBits & 0xFF000000) >> 24);
6061 major = (int)((encodedBits & 0x00FF0000) >> 16);
6062 minor = (int)((encodedBits & 0x0000FF00) >> 8);
6063 subminor = (int)((encodedBits & 0x000000FF) >> 0);