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);
198 /// <summary>Logic flag to signify that we should list assemblies at the method level, since there are multiple
199 /// assemblies for a given type/method.</summary>
200 public bool IsMultiAssembly {
202 return apistyle == "classic" || apistyle == "unified";
206 static List<string> droppedAssemblies = new List<string>();
208 public string PreserveTag { get; set; }
209 public static MDocUpdater Instance { get; private set; }
210 public static bool SwitchingToMagicTypes { get; private set; }
212 public override void Run (IEnumerable<string> args)
215 show_exceptions = DebugOutput;
216 var types = new List<string> ();
217 var p = new OptionSet () {
219 "Delete removed members from the XML files.",
220 v => delete = v != null },
222 "Document potential exceptions that members can generate. {SOURCES} " +
223 "is a comma-separated list of:\n" +
224 " asm Method calls in same assembly\n" +
225 " depasm Method calls in dependent assemblies\n" +
226 " all Record all possible exceptions\n" +
227 " added Modifier; only create <exception/>s\n" +
228 " for NEW types/members\n" +
229 "If nothing is specified, then only exceptions from the member will " +
231 v => exceptions = ParseExceptionLocations (v) },
233 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
236 case "ignore-missing-types":
237 ignore_missing_types = true;
239 case "no-assembly-versions":
240 no_assembly_versions = true;
243 throw new Exception ("Unsupported flag `" + v + "'.");
246 { "fignore-missing-types",
247 "Do not report an error if a --type=TYPE type\nwas not found.",
248 v => ignore_missing_types = v != null },
249 { "fno-assembly-versions",
250 "Do not generate //AssemblyVersion elements.",
251 v => no_assembly_versions = v != null },
253 "Import documentation from {FILE}.",
254 v => AddImporter (v) },
256 "Check for assembly references in {DIRECTORY}.",
257 v => assemblyResolver.AddSearchDirectory (v) },
259 "Ignored for compatibility with update-ecma-xml.",
262 "Root {DIRECTORY} to generate/update documentation.",
265 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
266 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
267 v => assemblyResolver.AddSearchDirectory (Path.GetDirectoryName (v)) },
269 "Manually specify the assembly {VERSION} that new members were added in.",
272 "Only update documentation for {TYPE}.",
273 v => types.Add (v) },
275 "When processing assembly {ASSEMBLY}, strip off leading namespace {PREFIX}:\n" +
276 " e.g. --dropns ASSEMBLY=PREFIX",
278 var parts = v.Split ('=');
279 if (parts.Length != 2) { Console.Error.WriteLine ("Invalid dropns input"); return; }
280 var assembly = Path.GetFileName (parts [0].Trim ());
281 var prefix = parts [1].Trim();
282 droppedAssemblies.Add (assembly);
283 droppedNamespace = prefix;
286 "If the new assembly is switching to 'magic types', then this switch should be defined.",
287 v => SwitchingToMagicTypes = true },
289 "Do not delete members that don't exist in the assembly, but rather mark them as preserved.",
290 v => PreserveTag = "true" },
292 "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.",
293 v => { apistyle = v.ToLowerInvariant (); }},
295 var assemblies = Parse (p, args, "update",
296 "[OPTIONS]+ ASSEMBLIES",
297 "Create or update documentation from ASSEMBLIES.");
298 if (assemblies == null)
300 if (assemblies.Count == 0)
301 Error ("No assemblies specified.");
303 // validation for the api-style parameter
304 if (apistyle == "classic")
306 else if (apistyle == "unified") {
307 if (!droppedAssemblies.Any ())
308 Error ("api-style 'unified' must also supply the 'dropns' parameter with at least one assembly and dropped namespace.");
309 } else if (!string.IsNullOrWhiteSpace (apistyle))
310 Error ("api-style '{0}' is not currently supported", apistyle);
313 foreach (var dir in assemblies
314 .Where (a => a.Contains (Path.DirectorySeparatorChar))
315 .Select (a => Path.GetDirectoryName (a)))
316 assemblyResolver.AddSearchDirectory (dir);
318 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
321 throw new InvalidOperationException("The --out option is required.");
323 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
325 // Store types that have been forwarded to avoid duplicate generation
326 GatherForwardedTypes ();
328 docEnum = docEnum ?? new DocumentationEnumerator ();
330 // PERFORM THE UPDATES
332 if (types.Count > 0) {
334 DoUpdateTypes (srcPath, types, srcPath);
337 else if (opts.@namespace != null)
338 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
339 Path.Combine (dest_dir, opts.@namespace));
342 DoUpdateAssemblies (srcPath, srcPath);
344 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
346 public static bool IsInAssemblies(string name) {
347 var query = Instance.assemblies.Where (a => a.MainModule.Name == name).ToArray ();
348 return query.Length > 0;
350 void AddImporter (string path)
353 XmlReader r = new XmlTextReader (path);
355 while (r.NodeType != XmlNodeType.Element) {
357 Error ("Unable to read XML file: {0}.", path);
359 if (r.LocalName == "doc") {
360 importers.Add (new MsxdocDocumentationImporter (path));
362 else if (r.LocalName == "Libraries") {
363 var ecmadocs = new XmlTextReader (path);
364 docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
365 importers.Add (new EcmaDocumentationImporter (ecmadocs));
368 Error ("Unsupported XML format within {0}.", path);
371 } catch (Exception e) {
372 Environment.ExitCode = 1;
373 Error ("Could not load XML file: {0}.", e.Message);
377 void GatherForwardedTypes ()
379 foreach (var asm in assemblies)
380 foreach (var type in asm.MainModule.ExportedTypes.Where (t => t.IsForwarder).Select (t => t.FullName))
381 forwardedTypes.Add (type);
384 static ExceptionLocations ParseExceptionLocations (string s)
386 ExceptionLocations loc = ExceptionLocations.Member;
389 foreach (var type in s.Split (',')) {
391 case "added": loc |= ExceptionLocations.AddedMembers; break;
392 case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
393 case "asm": loc |= ExceptionLocations.Assembly; break;
394 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
395 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
401 internal void Warning (string format, params object[] args)
403 Message (TraceLevel.Warning, "mdoc: " + format, args);
406 private AssemblyDefinition LoadAssembly (string name)
408 AssemblyDefinition assembly = null;
410 assembly = AssemblyDefinition.ReadAssembly (name, new ReaderParameters { AssemblyResolver = assemblyResolver });
411 } catch (System.IO.FileNotFoundException) { }
413 if (assembly == null)
414 throw new InvalidOperationException("Assembly " + name + " not found.");
419 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
420 OrderTypeAttributes (element);
421 XmlTextWriter writer = new XmlTextWriter(output);
422 writer.Formatting = Formatting.Indented;
423 writer.Indentation = 2;
424 writer.IndentChar = ' ';
425 element.WriteTo(writer);
429 private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
431 Action<string> creator = file => {
432 using (var writer = OpenWrite (file, mode))
436 MdocFile.UpdateFile (filename, creator);
439 private static void OrderTypeAttributes (XmlElement e)
441 foreach (XmlElement type in e.SelectNodes ("//Type")) {
442 OrderTypeAttributes (type.Attributes);
446 static readonly string[] TypeAttributeOrder = {
447 "Name", "FullName", "FullNameSP", "Maintainer"
450 private static void OrderTypeAttributes (XmlAttributeCollection c)
452 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
453 for (int i = 0; i < c.Count; ++i) {
454 XmlAttribute a = c [i];
455 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
456 if (a.Name == TypeAttributeOrder [j]) {
462 for (int i = attrs.Length-1; i >= 0; --i) {
463 XmlAttribute n = attrs [i];
466 XmlAttribute r = null;
467 for (int j = i+1; j < attrs.Length; ++j) {
468 if (attrs [j] != null) {
475 if (c [n.Name] != null) {
476 c.RemoveNamedItem (n.Name);
477 c.InsertBefore (n, r);
482 private XmlDocument CreateIndexStub()
484 XmlDocument index = new XmlDocument();
486 XmlElement index_root = index.CreateElement("Overview");
487 index.AppendChild(index_root);
489 if (assemblies.Count == 0)
490 throw new Exception ("No assembly");
492 XmlElement index_assemblies = index.CreateElement("Assemblies");
493 index_root.AppendChild(index_assemblies);
495 XmlElement index_remarks = index.CreateElement("Remarks");
496 index_remarks.InnerText = "To be added.";
497 index_root.AppendChild(index_remarks);
499 XmlElement index_copyright = index.CreateElement("Copyright");
500 index_copyright.InnerText = "To be added.";
501 index_root.AppendChild(index_copyright);
503 XmlElement index_types = index.CreateElement("Types");
504 index_root.AppendChild(index_types);
509 private static void WriteNamespaceStub(string ns, string outdir) {
510 XmlDocument index = new XmlDocument();
512 XmlElement index_root = index.CreateElement("Namespace");
513 index.AppendChild(index_root);
515 index_root.SetAttribute("Name", ns);
517 XmlElement index_docs = index.CreateElement("Docs");
518 index_root.AppendChild(index_docs);
520 XmlElement index_summary = index.CreateElement("summary");
521 index_summary.InnerText = "To be added.";
522 index_docs.AppendChild(index_summary);
524 XmlElement index_remarks = index.CreateElement("remarks");
525 index_remarks.InnerText = "To be added.";
526 index_docs.AppendChild(index_remarks);
528 WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
529 writer => WriteXml (index.DocumentElement, writer));
532 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
534 var index = CreateIndexForTypes (dest);
536 var found = new HashSet<string> ();
537 foreach (AssemblyDefinition assembly in assemblies) {
538 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames)) {
539 string relpath = DoUpdateType (type, basepath, dest);
543 found.Add (type.FullName);
548 index.Add (assembly);
556 if (ignore_missing_types)
559 var notFound = from n in typenames where !found.Contains (n) select n;
561 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
564 class IndexForTypes {
570 XmlElement index_types;
571 XmlElement index_assemblies;
573 public IndexForTypes (MDocUpdater app, string indexFile, XmlDocument index)
576 this.indexFile = indexFile;
579 index_types = WriteElement (index.DocumentElement, "Types");
580 index_assemblies = WriteElement (index.DocumentElement, "Assemblies");
583 public void Add (AssemblyDefinition assembly)
585 if (index_assemblies.SelectSingleNode ("Assembly[@Name='" + assembly.Name.Name + "']") != null)
588 app.AddIndexAssembly (assembly, index_assemblies);
591 public void Add (TypeDefinition type)
593 app.AddIndexType (type, index_types);
598 SortIndexEntries (index_types);
599 WriteFile (indexFile, FileMode.Create,
600 writer => WriteXml (index.DocumentElement, writer));
604 IndexForTypes CreateIndexForTypes (string dest)
606 string indexFile = Path.Combine (dest, "index.xml");
607 if (File.Exists (indexFile))
609 return new IndexForTypes (this, indexFile, CreateIndexStub ());
612 /// <summary>Constructs the presumed path to the type's documentation file</summary>
613 /// <returns><c>true</c>, if the type file was found, <c>false</c> otherwise.</returns>
614 /// <param name="result">A typle that contains 1) the 'reltypefile', 2) the 'typefile', and 3) the file info</param>
615 bool TryFindTypeFile(string nsname, string typename, string basepath, out Tuple<string, string, FileInfo> result) {
616 string reltypefile = DocUtils.PathCombine (nsname, typename + ".xml");
617 string typefile = Path.Combine (basepath, reltypefile);
618 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
620 result = new Tuple<string, string, FileInfo> (reltypefile, typefile, file);
625 public string DoUpdateType (TypeDefinition type, string basepath, string dest)
627 if (type.Namespace == null)
628 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
630 if (!IsPublic (type))
633 // Must get the A+B form of the type name.
634 string typename = GetTypeFileName(type);
635 string nsname = DocUtils.GetNamespace (type);
637 // Find the file, if it exists
638 string[] searchLocations = new string[] {
642 if (MDocUpdater.HasDroppedNamespace (type)) {
643 // If dropping namespace, types may have moved into a couple of different places.
644 var newSearchLocations = searchLocations.Union (new string[] {
645 string.Format ("{0}.{1}", droppedNamespace, nsname),
646 nsname.Replace (droppedNamespace + ".", string.Empty),
647 MDocUpdater.droppedNamespace
650 searchLocations = newSearchLocations.ToArray ();
653 string reltypefile="", typefile="";
654 System.IO.FileInfo file = null;
656 foreach (var f in searchLocations) {
657 Tuple<string, string, FileInfo> result;
658 bool fileExists = TryFindTypeFile (f, typename, basepath, out result);
661 reltypefile = result.Item1;
662 typefile = result.Item2;
669 if (file == null || !file.Exists) {
670 // we were not able to find a file, let's use the original type informatio.
671 // so that we create the stub in the right place.
672 Tuple<string, string, FileInfo> result;
673 TryFindTypeFile (nsname, typename, basepath, out result);
675 reltypefile = result.Item1;
676 typefile = result.Item2;
680 string output = null;
683 } else if (dest == "-") {
686 output = Path.Combine (dest, reltypefile);
689 if (file != null && file.Exists) {
691 XmlDocument basefile = new XmlDocument();
693 basefile.Load(typefile);
694 } catch (Exception e) {
695 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
698 DoUpdateType2("Updating", basefile, type, output, false);
701 XmlElement td = StubType(type, output);
708 public void DoUpdateNS (string ns, string nspath, string outpath)
710 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
711 AssemblyDefinition assembly = assemblies [0];
713 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
714 XmlDocument basefile = new XmlDocument();
715 string typefile = Path.Combine(nspath, file.Name);
717 basefile.Load(typefile);
718 } catch (Exception e) {
719 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
723 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
724 TypeDefinition type = assembly.GetType(typename);
727 if (!string.IsNullOrWhiteSpace (droppedNamespace)) {
728 string nameWithNs = string.Format ("{0}.{1}", droppedNamespace, typename);
729 type = assembly.GetType (nameWithNs);
731 Warning ("Type no longer in assembly: " + typename);
738 seenTypes[type] = seenTypes;
739 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false);
742 // Stub types not in the directory
743 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
744 if (type.Namespace != ns || seenTypes.ContainsKey(type))
747 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"));
748 if (td == null) continue;
752 private static string GetTypeFileName (TypeReference type)
754 return filenameFormatter.GetName (type);
757 public static string GetTypeFileName (string typename)
759 StringBuilder filename = new StringBuilder (typename.Length);
763 for (int i = 0; i < typename.Length; ++i) {
764 char c = typename [i];
773 filename.Append ('`').Append ((numArgs+1).ToString());
788 return filename.ToString ();
791 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
793 XmlElement index_assembly = null;
795 index_assembly = (XmlElement)parent.SelectSingleNode ("Assembly[@Name='"+ assembly.Name.Name +"']");
797 if (index_assembly == null)
798 index_assembly = parent.OwnerDocument.CreateElement ("Assembly");
800 index_assembly.SetAttribute ("Name", assembly.Name.Name);
801 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
803 AssemblyNameDefinition name = assembly.Name;
804 if (name.HasPublicKey) {
805 XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
806 var key = new StringBuilder (name.PublicKey.Length*3 + 2);
808 foreach (byte b in name.PublicKey)
809 key.AppendFormat ("{0,2:x2} ", b);
811 pubkey.InnerText = key.ToString ();
812 index_assembly.AppendChild (pubkey);
815 if (!string.IsNullOrEmpty (name.Culture)) {
816 XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
817 culture.InnerText = name.Culture;
818 index_assembly.AppendChild (culture);
821 MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
822 parent.AppendChild(index_assembly);
825 private void AddIndexType (TypeDefinition type, XmlElement index_types)
827 string typename = GetTypeFileName(type);
829 // Add namespace and type nodes into the index file as needed
830 string ns = DocUtils.GetNamespace (type);
831 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode ("Namespace[@Name='" + ns + "']");
832 if (nsnode == null) {
833 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
834 nsnode.SetAttribute ("Name", ns);
835 index_types.AppendChild (nsnode);
837 string doc_typename = GetDocTypeName (type);
838 XmlElement typenode = (XmlElement) nsnode.SelectSingleNode ("Type[@Name='" + typename + "']");
839 if (typenode == null) {
840 typenode = index_types.OwnerDocument.CreateElement ("Type");
841 typenode.SetAttribute ("Name", typename);
842 nsnode.AppendChild (typenode);
844 if (typename != doc_typename)
845 typenode.SetAttribute("DisplayName", doc_typename);
847 typenode.RemoveAttribute("DisplayName");
849 typenode.SetAttribute ("Kind", GetTypeKind (type));
852 private void DoUpdateAssemblies (string source, string dest)
854 string indexfile = dest + "/index.xml";
856 if (System.IO.File.Exists(indexfile)) {
857 index = new XmlDocument();
858 index.Load(indexfile);
861 ClearElement(index.DocumentElement, "Assembly");
862 ClearElement(index.DocumentElement, "Attributes");
864 index = CreateIndexStub();
867 string defaultTitle = "Untitled";
868 if (assemblies.Count == 1)
869 defaultTitle = assemblies[0].Name.Name;
870 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
872 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
873 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
874 if (!IsMultiAssembly)
875 index_assemblies.RemoveAll ();
878 HashSet<string> goodfiles = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
880 foreach (AssemblyDefinition assm in assemblies) {
881 AddIndexAssembly (assm, index_assemblies);
882 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
885 SortIndexEntries (index_types);
887 CleanupFiles (dest, goodfiles);
888 CleanupIndexTypes (index_types, goodfiles);
889 CleanupExtensions (index_types);
891 WriteFile (indexfile, FileMode.Create,
892 writer => WriteXml(index.DocumentElement, writer));
895 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
897 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
899 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
900 string typename = GetTypeFileName(type);
901 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0 || forwardedTypes.Contains (type.FullName))
904 string reltypepath = DoUpdateType (type, source, dest);
905 if (reltypepath == null)
908 // Add namespace and type nodes into the index file as needed
909 AddIndexType (type, index_types);
911 // Ensure the namespace index file exists
912 string namespaceToUse = type.Namespace;
913 if (HasDroppedNamespace(assembly)) {
914 namespaceToUse = string.Format ("{0}.{1}", droppedNamespace, namespaceToUse);
916 string onsdoc = DocUtils.PathCombine (dest, namespaceToUse + ".xml");
917 string nsdoc = DocUtils.PathCombine (dest, "ns-" + namespaceToUse + ".xml");
918 if (File.Exists (onsdoc)) {
919 File.Move (onsdoc, nsdoc);
922 if (!File.Exists (nsdoc)) {
923 Console.WriteLine("New Namespace File: " + type.Namespace);
924 WriteNamespaceStub(namespaceToUse, dest);
927 goodfiles.Add (reltypepath);
931 private static void SortIndexEntries (XmlElement indexTypes)
933 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
934 XmlNodeComparer c = new AttributeNameComparer ();
935 SortXmlNodes (indexTypes, namespaces, c);
937 for (int i = 0; i < namespaces.Count; ++i)
938 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
941 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
943 MyXmlNodeList l = new MyXmlNodeList (children.Count);
944 for (int i = 0; i < children.Count; ++i)
945 l.Add (children [i]);
947 for (int i = l.Count - 1; i > 0; --i) {
948 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
952 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
954 public abstract int Compare (XmlNode x, XmlNode y);
956 public int Compare (object x, object y)
958 return Compare ((XmlNode) x, (XmlNode) y);
962 class AttributeNameComparer : XmlNodeComparer {
965 public AttributeNameComparer ()
970 public AttributeNameComparer (string attribute)
972 this.attribute = attribute;
975 public override int Compare (XmlNode x, XmlNode y)
977 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
981 class VersionComparer : XmlNodeComparer {
982 public override int Compare (XmlNode x, XmlNode y)
984 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
985 string a = GetVersion (x.InnerText);
986 string b = GetVersion (y.InnerText);
987 return new Version (a).CompareTo (new Version (b));
990 static string GetVersion (string v)
992 int n = v.IndexOf ("x");
995 return v.Substring (0, n-1);
999 private static string GetTypeKind (TypeDefinition type)
1002 return "Enumeration";
1003 if (type.IsValueType)
1005 if (type.IsInterface)
1007 if (DocUtils.IsDelegate (type))
1009 if (type.IsClass || type.FullName == "System.Enum") // FIXME
1011 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
1014 public static bool IsPublic (TypeDefinition type)
1016 TypeDefinition decl = type;
1017 while (decl != null) {
1018 if (!(decl.IsPublic || decl.IsNestedPublic ||
1019 decl.IsNestedFamily || decl.IsNestedFamily || decl.IsNestedFamilyOrAssembly)) {
1022 decl = (TypeDefinition) decl.DeclaringType;
1027 private void CleanupFiles (string dest, HashSet<string> goodfiles)
1029 // Look for files that no longer correspond to types
1030 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
1031 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
1032 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
1033 if (!goodfiles.Contains (relTypeFile)) {
1034 XmlDocument doc = new XmlDocument ();
1035 doc.Load (typefile.FullName);
1036 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
1037 var assemblyNameNode = doc.SelectSingleNode ("/Type/AssemblyInfo/AssemblyName");
1038 if (assemblyNameNode == null){
1039 Warning ("Did not find /Type/AssemblyInfo/AssemblyName on {0}", typefile.FullName);
1042 string assemblyName = assemblyNameNode.InnerText;
1043 AssemblyDefinition assembly = assemblies.FirstOrDefault (a => a.Name.Name == assemblyName);
1045 Action saveDoc = () => {
1046 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
1047 WriteXml(doc.DocumentElement, writer);
1050 if (e != null && !no_assembly_versions && assembly != null && assemblyName != null && UpdateAssemblyVersions (e, assembly, GetAssemblyVersions(assemblyName), false)) {
1052 goodfiles.Add (relTypeFile);
1056 Action actuallyDelete = () => {
1057 string newname = typefile.FullName + ".remove";
1058 try { System.IO.File.Delete (newname); } catch (Exception) { Warning ("Unable to delete existing file: {0}", newname); }
1059 try { typefile.MoveTo (newname); } catch (Exception) { Warning ("Unable to rename to: {0}", newname); }
1060 Console.WriteLine ("Class no longer present; file renamed: " + Path.Combine (nsdir.Name, typefile.Name));
1063 if (string.IsNullOrWhiteSpace (PreserveTag)) { // only do this if there was not a -preserve
1066 var unifiedAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[@apistyle='unified']");
1067 var classicAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[not(@apistyle) or @apistyle='classic']");
1068 var unifiedMembers = doc.SelectNodes ("//Member[@apistyle='unified']|//Member/AssemblyInfo[@apistyle='unified']");
1069 var classicMembers = doc.SelectNodes ("//Member[@apistyle='classic']|//Member/AssemblyInfo[@apistyle='classic']");
1070 bool isUnifiedRun = HasDroppedAnyNamespace ();
1071 bool isClassicOrNormalRun = !isUnifiedRun;
1073 Action<XmlNode, ApiStyle> removeStyles = (x, style) => {
1074 var styledNodes = doc.SelectNodes("//*[@apistyle='"+ style.ToString ().ToLowerInvariant () +"']");
1075 if (styledNodes != null && styledNodes.Count > 0) {
1076 foreach(var node in styledNodes.Cast<XmlNode> ()) {
1077 node.ParentNode.RemoveChild (node);
1082 if (isClassicOrNormalRun) {
1083 if (unifiedAssemblyNode != null || unifiedMembers.Count > 0) {
1084 Warning ("*** this type is marked as unified, not deleting during this run: {0}", typefile.FullName);
1085 // if truly removed from both assemblies, it will be removed fully during the unified run
1086 removeStyles (doc, ApiStyle.Classic);
1089 // we should be safe to delete here because it was not marked as a unified assembly
1094 if (classicAssemblyNode != null || classicMembers.Count > 0) {
1095 Warning ("*** this type is marked as classic, not deleting {0}", typefile.FullName);
1098 // safe to delete because it wasn't marked as a classic assembly, so the type is gone in both.
1108 private static TextWriter OpenWrite (string path, FileMode mode)
1110 var w = new StreamWriter (
1111 new FileStream (path, mode),
1112 new UTF8Encoding (false)
1118 private string[] GetAssemblyVersions (string assemblyName)
1120 return (from a in assemblies
1121 where a.Name.Name == assemblyName
1122 select GetAssemblyVersion (a)).ToArray ();
1125 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
1127 // Look for type nodes that no longer correspond to types
1128 MyXmlNodeList remove = new MyXmlNodeList ();
1129 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
1130 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
1131 if (!goodfiles.Contains (fulltypename)) {
1132 remove.Add (typenode);
1135 foreach (XmlNode n in remove)
1136 n.ParentNode.RemoveChild (n);
1139 private void CleanupExtensions (XmlElement index_types)
1141 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
1142 if (extensionMethods.Count == 0) {
1145 index_types.SelectSingleNode ("/Overview").RemoveChild (e);
1149 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
1150 index_types.SelectSingleNode ("/Overview").AppendChild (e);
1154 extensionMethods.Sort (DefaultExtensionMethodComparer);
1155 foreach (XmlNode m in extensionMethods) {
1156 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
1160 class ExtensionMethodComparer : XmlNodeComparer {
1161 public override int Compare (XmlNode x, XmlNode y)
1163 XmlNode xLink = x.SelectSingleNode ("Member/Link");
1164 XmlNode yLink = y.SelectSingleNode ("Member/Link");
1166 int n = xLink.Attributes ["Type"].Value.CompareTo (
1167 yLink.Attributes ["Type"].Value);
1170 n = xLink.Attributes ["Member"].Value.CompareTo (
1171 yLink.Attributes ["Member"].Value);
1172 if (n == 0 && !object.ReferenceEquals (x, y))
1173 throw new InvalidOperationException ("Duplicate extension method found!");
1178 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
1180 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince)
1182 Console.WriteLine(message + ": " + type.FullName);
1184 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
1186 // Update type metadata
1187 UpdateType(basefile.DocumentElement, type);
1189 // Update existing members. Delete member nodes that no longer should be there,
1190 // and remember what members are already documented so we don't add them again.
1192 MyXmlNodeList todelete = new MyXmlNodeList ();
1194 foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
1195 XmlElement oldmember = info.Node;
1196 MemberReference oldmember2 = info.Member;
1198 if (info.Member != null && info.Node != null) {
1199 // Check for an error condition where the xml MemberName doesn't match the matched member
1200 var memberName = GetMemberName (info.Member);
1201 var memberAttribute = info.Node.Attributes ["MemberName"];
1202 if (memberAttribute == null || (memberAttribute.Value != memberName && memberAttribute.Value.Split (',').Length != memberName.Split (',').Length)) {
1203 oldmember.SetAttribute ("MemberName", memberName);
1207 string sig = oldmember2 != null ? memberFormatters [0].GetDeclaration (oldmember2) : null;
1209 // Interface implementations and overrides are deleted from the docs
1210 // unless the overrides option is given.
1211 if (oldmember2 != null && sig == null)
1214 // Deleted (or signature changed)
1215 if (oldmember2 == null) {
1216 if (!no_assembly_versions && UpdateAssemblyVersions (oldmember, type.Module.Assembly, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
1219 DeleteMember ("Member Removed", output, oldmember, todelete, type);
1224 if (seenmembers.ContainsKey (sig)) {
1225 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
1226 // ignore, already seen
1228 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
1229 DeleteMember ("Duplicate Member Found", output, oldmember, todelete, type);
1231 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
1235 // Update signature information
1238 // get all apistyles of sig from info.Node
1239 var styles = oldmember.GetElementsByTagName ("MemberSignature").Cast<XmlElement> ()
1240 .Where (x => x.GetAttribute ("Language") == "C#" && !seenmembers.ContainsKey(x.GetAttribute("Value")))
1241 .Select (x => x.GetAttribute ("Value"));
1243 foreach (var stylesig in styles) {
1244 seenmembers.Add (stylesig, oldmember);
1247 foreach (XmlElement oldmember in todelete)
1248 oldmember.ParentNode.RemoveChild (oldmember);
1251 if (!DocUtils.IsDelegate (type)) {
1252 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
1253 var typemembers = type.GetMembers()
1255 if (m is TypeDefinition) return false;
1256 string sig = memberFormatters [0].GetDeclaration (m);
1257 if (sig == null) return false;
1258 if (seenmembers.ContainsKey(sig)) return false;
1260 // Verify that the member isn't an explicitly implemented
1261 // member of an internal interface, in which case we shouldn't return true.
1262 MethodDefinition methdef = null;
1263 if (m is MethodDefinition)
1264 methdef = m as MethodDefinition;
1265 else if (m is PropertyDefinition) {
1266 var prop = m as PropertyDefinition;
1267 methdef = prop.GetMethod ?? prop.SetMethod;
1270 if (methdef != null) {
1271 TypeReference iface;
1272 MethodReference imethod;
1274 if (methdef.Overrides.Count == 1) {
1275 DocUtils.GetInfoForExplicitlyImplementedMethod (methdef, out iface, out imethod);
1276 if (!IsPublic (iface.Resolve ())) return false;
1283 foreach (MemberReference m in typemembers) {
1284 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
1285 if (mm == null) continue;
1287 if (MDocUpdater.SwitchingToMagicTypes || MDocUpdater.HasDroppedNamespace (m)) {
1288 // this is a unified style API that obviously doesn't exist in the classic API. Let's mark
1289 // it with apistyle="unified", so that it's not displayed for classic style APIs
1290 mm.AddApiStyle (ApiStyle.Unified);
1293 members.AppendChild( mm );
1295 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
1300 // Import code snippets from files
1301 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
1302 if (!(code is XmlElement)) continue;
1303 string file = ((XmlElement)code).GetAttribute("src");
1304 string lang = ((XmlElement)code).GetAttribute("lang");
1306 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
1308 code.InnerText = src;
1312 if (insertSince && since != null) {
1313 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
1314 docs.AppendChild (CreateSinceNode (basefile));
1318 XmlElement d = basefile.DocumentElement ["Docs"];
1319 XmlElement m = basefile.DocumentElement ["Members"];
1320 if (d != null && m != null)
1321 basefile.DocumentElement.InsertBefore (
1322 basefile.DocumentElement.RemoveChild (d), m);
1323 SortTypeMembers (m);
1327 WriteXml(basefile.DocumentElement, Console.Out);
1329 FileInfo file = new FileInfo (output);
1330 if (!file.Directory.Exists) {
1331 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
1332 file.Directory.Create ();
1334 WriteFile (output, FileMode.Create,
1335 writer => WriteXml(basefile.DocumentElement, writer));
1339 private string GetCodeSource (string lang, string file)
1342 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
1343 // Grab the specified region
1344 string region = "#region " + file.Substring (anchorStart + 4);
1345 file = file.Substring (0, anchorStart + 3);
1347 using (StreamReader reader = new StreamReader (file)) {
1349 StringBuilder src = new StringBuilder ();
1351 while ((line = reader.ReadLine ()) != null) {
1352 if (line.Trim() == region) {
1353 indent = line.IndexOf (region);
1356 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
1361 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
1364 return src.ToString ();
1366 } catch (Exception e) {
1367 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
1368 file, region, show_exceptions ? e.ToString () : e.Message);
1373 using (StreamReader reader = new StreamReader (file))
1374 return reader.ReadToEnd ();
1375 } catch (Exception e) {
1376 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
1381 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete, TypeDefinition type)
1383 string format = output != null
1384 ? "{0}: File='{1}'; Signature='{4}'"
1385 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1386 string signature = member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value;
1390 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1391 member.Attributes ["MemberName"].Value,
1394 // Identify all of the different states that could affect our decision to delete the member
1395 bool shouldPreserve = !string.IsNullOrWhiteSpace (PreserveTag);
1396 bool hasContent = MemberDocsHaveUserContent (member);
1397 bool shouldDelete = !shouldPreserve && (delete || !hasContent);
1399 bool unifiedRun = HasDroppedNamespace (type);
1401 var classicAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[not(@apistyle) or @apistyle='classic']");
1402 bool nodeIsClassic = classicAssemblyInfo != null || member.HasApiStyle (ApiStyle.Classic);
1403 var unifiedAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']");
1404 bool nodeIsUnified = unifiedAssemblyInfo != null || member.HasApiStyle (ApiStyle.Unified);
1406 Action actuallyDelete = () => {
1407 todelete.Add (member);
1411 if (!shouldDelete) {
1412 // explicitly not deleting
1413 string message = shouldPreserve ?
1414 "Not deleting '{0}' due to --preserve." :
1415 "Not deleting '{0}'; must be enabled with the --delete option";
1416 Warning (message, signature);
1417 } else if (unifiedRun && nodeIsClassic) {
1418 // this is a unified run, and the member doesn't exist, but is marked as being in the classic assembly.
1419 member.RemoveApiStyle (ApiStyle.Unified);
1420 member.AddApiStyle (ApiStyle.Classic);
1421 Warning ("Not removing '{0}' since it's still in the classic assembly.", signature);
1422 } else if (unifiedRun && !nodeIsClassic) {
1423 // unified run, and the node is not classic, which means it doesn't exist anywhere.
1426 if (!isClassicRun || (isClassicRun && !nodeIsClassic && !nodeIsUnified)) { // regular codepath (ie. not classic/unified)
1428 } else { // this is a classic run
1429 Warning ("Removing classic from '{0}' ... will be removed in the unified run if not present there.", signature);
1430 member.RemoveApiStyle (ApiStyle.Classic);
1431 if (classicAssemblyInfo != null) {
1432 member.RemoveChild (classicAssemblyInfo);
1438 class MemberComparer : XmlNodeComparer {
1439 public override int Compare (XmlNode x, XmlNode y)
1442 string xMemberName = x.Attributes ["MemberName"].Value;
1443 string yMemberName = y.Attributes ["MemberName"].Value;
1445 // generic methods *end* with '>'
1446 // it's possible for explicitly implemented generic interfaces to
1447 // contain <...> without being a generic method
1448 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1449 (r = xMemberName.CompareTo (yMemberName)) != 0)
1453 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1454 xMemberName = xMemberName.Substring (0, lt);
1455 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1456 yMemberName = yMemberName.Substring (0, lt);
1457 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1460 // if @MemberName matches, then it's either two different types of
1461 // members sharing the same name, e.g. field & property, or it's an
1462 // overloaded method.
1463 // for different type, sort based on MemberType value.
1464 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1465 y.SelectSingleNode ("MemberType").InnerText);
1469 // same type -- must be an overloaded method. Sort based on type
1470 // parameter count, then parameter count, then by the parameter
1472 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1473 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1474 if (xTypeParams.Count != yTypeParams.Count)
1475 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1476 for (int i = 0; i < xTypeParams.Count; ++i) {
1477 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1478 yTypeParams [i].Attributes ["Name"].Value);
1483 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1484 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1485 if (xParams.Count != yParams.Count)
1486 return xParams.Count <= yParams.Count ? -1 : 1;
1487 for (int i = 0; i < xParams.Count; ++i) {
1488 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1489 yParams [i].Attributes ["Type"].Value);
1493 // all parameters match, but return value might not match if it was
1494 // changed between one version and another.
1495 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1496 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1497 if (xReturn != null && yReturn != null) {
1498 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1507 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1509 private static void SortTypeMembers (XmlNode members)
1511 if (members == null)
1513 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1516 private static bool MemberDocsHaveUserContent (XmlNode e)
1518 e = (XmlElement)e.SelectSingleNode("Docs");
1519 if (e == null) return false;
1520 foreach (XmlElement d in e.SelectNodes("*"))
1521 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1526 // UPDATE HELPER FUNCTIONS
1528 // CREATE A STUB DOCUMENTATION FILE
1530 public XmlElement StubType (TypeDefinition type, string output)
1532 string typesig = typeFormatters [0].GetDeclaration (type);
1533 if (typesig == null) return null; // not publicly visible
1535 XmlDocument doc = new XmlDocument();
1536 XmlElement root = doc.CreateElement("Type");
1537 doc.AppendChild (root);
1539 DoUpdateType2 ("New Type", doc, type, output, true);
1544 private XmlElement CreateSinceNode (XmlDocument doc)
1546 XmlElement s = doc.CreateElement ("since");
1547 s.SetAttribute ("version", since);
1551 // STUBBING/UPDATING FUNCTIONS
1553 public void UpdateType (XmlElement root, TypeDefinition type)
1555 root.SetAttribute("Name", GetDocTypeName (type));
1556 root.SetAttribute("FullName", GetDocTypeFullName (type));
1558 foreach (MemberFormatter f in typeFormatters) {
1559 string element = "TypeSignature[@Language='" + f.Language + "']";
1560 string valueToUse = f.GetDeclaration (type);
1563 root.SelectNodes (element).Cast<XmlElement> ().ToArray (),
1564 x => x.GetAttribute ("Value") == valueToUse,
1565 x => x.SetAttribute ("Value", valueToUse),
1567 var node = WriteElementAttribute (root, element, "Language", f.Language, forceNewElement: true);
1568 var newnode = WriteElementAttribute (root, node, "Value", valueToUse);
1574 AddAssemblyNameToNode (root, type);
1576 string assemblyInfoNodeFilter = MDocUpdater.HasDroppedNamespace (type) ? "[@apistyle='unified']" : "[not(@apistyle) or @apistyle='classic']";
1577 Func<XmlElement, bool> assemblyFilter = x => x.SelectSingleNode ("AssemblyName").InnerText == type.Module.Assembly.Name.Name;
1578 foreach(var ass in root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ().Where (assemblyFilter))
1580 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1581 if (!no_assembly_versions) {
1582 UpdateAssemblyVersions (ass, type, true);
1585 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1586 foreach (var version in versions)
1587 ass.RemoveChild (version);
1589 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1590 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1592 ClearElement(ass, "AssemblyCulture");
1595 // Why-oh-why do we put assembly attributes in each type file?
1596 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1597 // since they're outdated in current docs, and a waste of space.
1598 //MakeAttributes(ass, type.Assembly, true);
1599 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1600 if (assattrs != null)
1601 ass.RemoveChild(assattrs);
1603 NormalizeWhitespace(ass);
1606 if (type.IsGenericType ()) {
1607 MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
1609 ClearElement(root, "TypeParameters");
1612 if (type.BaseType != null) {
1613 XmlElement basenode = WriteElement(root, "Base");
1615 string basetypename = GetDocTypeFullName (type.BaseType);
1616 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1617 WriteElementText(root, "Base/BaseTypeName", basetypename);
1619 // Document how this type instantiates the generic parameters of its base type
1620 TypeReference origBase = type.BaseType.GetElementType ();
1621 if (origBase.IsGenericType ()) {
1622 ClearElement(basenode, "BaseTypeArguments");
1623 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1624 IList<TypeReference> baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1625 IList<GenericParameter> baseGenParams = origBase.GenericParameters;
1626 if (baseGenArgs.Count != baseGenParams.Count)
1627 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1628 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1629 GenericParameter param = baseGenParams [i];
1630 TypeReference value = baseGenArgs [i];
1632 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1633 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1634 bta.AppendChild(arg);
1635 arg.SetAttribute ("TypeParamName", param.Name);
1636 arg.InnerText = GetDocTypeFullName (value);
1640 ClearElement(root, "Base");
1643 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1644 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1645 List<string> interface_names = userInterfaces
1646 .Select (iface => GetDocTypeFullName (iface))
1650 XmlElement interfaces = WriteElement(root, "Interfaces");
1651 interfaces.RemoveAll();
1652 foreach (string iname in interface_names) {
1653 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1654 interfaces.AppendChild(iface);
1655 WriteElementText(iface, "InterfaceName", iname);
1658 ClearElement(root, "Interfaces");
1661 MakeAttributes (root, GetCustomAttributes (type), type);
1663 if (DocUtils.IsDelegate (type)) {
1664 MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
1665 var member = type.GetMethod ("Invoke");
1666 MakeParameters(root, member, member.Parameters);
1667 MakeReturnValue(root, member);
1670 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1671 MakeDocNode (typeInfo);
1673 if (!DocUtils.IsDelegate (type))
1674 WriteElement (root, "Members");
1676 OrderTypeNodes (root, root.ChildNodes);
1677 NormalizeWhitespace(root);
1680 /// <summary>Adds an AssemblyInfo with AssemblyName node to an XmlElement.</summary>
1681 /// <returns>The assembly that was either added, or was already present</returns>
1682 XmlElement AddAssemblyNameToNode (XmlElement root, TypeDefinition type)
1684 return AddAssemblyNameToNode (root, type.Module);
1687 /// <summary>Adds an AssemblyInfo with AssemblyName node to an XmlElement.</summary>
1688 /// <returns>The assembly that was either added, or was already present</returns>
1689 XmlElement AddAssemblyNameToNode (XmlElement root, ModuleDefinition module)
1691 Func<XmlElement, bool> assemblyFilter = x => {
1692 var existingName = x.SelectSingleNode ("AssemblyName");
1694 bool apiStyleMatches = true;
1695 string currentApiStyle = x.GetAttribute ("apistyle");
1696 if ((HasDroppedNamespace (module) && !string.IsNullOrWhiteSpace (currentApiStyle) && currentApiStyle != "unified") ||
1697 (isClassicRun && (string.IsNullOrWhiteSpace (currentApiStyle) || currentApiStyle != "classic"))) {
1698 apiStyleMatches = false;
1700 return apiStyleMatches && (existingName == null || (existingName != null && existingName.InnerText == module.Assembly.Name.Name));
1703 return AddAssemblyXmlNode (
1704 root.SelectNodes ("AssemblyInfo").Cast<XmlElement> ().ToArray (),
1705 assemblyFilter, x => WriteElementText (x, "AssemblyName", module.Assembly.Name.Name),
1707 XmlElement ass = WriteElement (root, "AssemblyInfo", forceNewElement: true);
1709 if (MDocUpdater.HasDroppedNamespace (module))
1710 ass.AddApiStyle (ApiStyle.Unified);
1712 ass.AddApiStyle (ApiStyle.Classic);
1717 static readonly string[] TypeNodeOrder = {
1721 "ThreadingSafetyStatement",
1722 "ThreadSafetyStatement",
1734 static void OrderTypeNodes (XmlNode member, XmlNodeList children)
1736 ReorderNodes (member, children, TypeNodeOrder);
1739 internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1741 List<T> l = new List<T> (list);
1746 private void UpdateMember (DocsNodeInfo info)
1748 XmlElement me = (XmlElement) info.Node;
1749 MemberReference mi = info.Member;
1751 foreach (MemberFormatter f in memberFormatters) {
1752 string element = "MemberSignature[@Language='" + f.Language + "']";
1754 var valueToUse = f.GetDeclaration (mi);
1757 me.SelectNodes (element).Cast<XmlElement> ().ToArray(),
1758 x => x.GetAttribute("Value") == valueToUse,
1759 x => x.SetAttribute ("Value", valueToUse),
1761 var node = WriteElementAttribute (me, element, "Language", f.Language, forceNewElement:true);
1762 var newNode = WriteElementAttribute (me, node, "Value", valueToUse);
1769 WriteElementText(me, "MemberType", GetMemberType(mi));
1771 if (!no_assembly_versions) {
1772 if (!IsMultiAssembly)
1773 UpdateAssemblyVersions (me, mi, true);
1775 var node = AddAssemblyNameToNode (me, mi.Module);
1777 UpdateAssemblyVersionForAssemblyInfo (node, me, new[] { GetAssemblyVersion (mi.Module.Assembly) }, add: true);
1781 ClearElement (me, "AssemblyInfo");
1784 MakeAttributes (me, GetCustomAttributes (mi), mi.DeclaringType);
1786 MakeReturnValue(me, mi, MDocUpdater.HasDroppedNamespace(mi));
1787 if (mi is MethodReference) {
1788 MethodReference mb = (MethodReference) mi;
1789 if (mb.IsGenericMethod ())
1790 MakeTypeParameters (me, mb.GenericParameters, mi, MDocUpdater.HasDroppedNamespace(mi));
1792 MakeParameters(me, mi, MDocUpdater.HasDroppedNamespace(mi));
1795 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1796 WriteElementText(me, "MemberValue", fieldValue);
1798 info.Node = WriteElement (me, "Docs");
1800 OrderMemberNodes (me, me.ChildNodes);
1801 UpdateExtensionMethods (me, info);
1804 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, MemberReference member) {
1805 AddXmlNode (relevant, valueMatches, setValue, makeNewNode, member.Module);
1808 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, TypeDefinition type) {
1809 AddXmlNode (relevant, valueMatches, setValue, makeNewNode, type.Module);
1812 static XmlElement AddAssemblyXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, ModuleDefinition module)
1814 bool isUnified = MDocUpdater.HasDroppedNamespace (module);
1815 XmlElement thisAssemblyNode = relevant.FirstOrDefault (valueMatches);
1816 if (thisAssemblyNode == null) {
1817 thisAssemblyNode = makeNewNode ();
1819 setValue (thisAssemblyNode);
1822 thisAssemblyNode.AddApiStyle (ApiStyle.Unified);
1824 foreach (var otherNodes in relevant.Where (n => n != thisAssemblyNode && n.DoesNotHaveApiStyle (ApiStyle.Unified))) {
1825 otherNodes.AddApiStyle (ApiStyle.Classic);
1828 return thisAssemblyNode;
1831 /// <summary>Adds an xml node, reusing the node if it's available</summary>
1832 /// <param name="relevant">The existing set of nodes</param>
1833 /// <param name="valueMatches">Checks to see if the node's value matches what you're trying to write.</param>
1834 /// <param name="setValue">Sets the node's value</param>
1835 /// <param name="makeNewNode">Creates a new node, if valueMatches returns false.</param>
1836 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, ModuleDefinition module)
1838 bool shouldDuplicate = MDocUpdater.HasDroppedNamespace (module);
1839 var styleToUse = shouldDuplicate ? ApiStyle.Unified : ApiStyle.Classic;
1840 var existing = relevant;
1842 bool addedOldApiStyle = false;
1844 if (shouldDuplicate) {
1845 existing = existing.Where (n => n.HasApiStyle (styleToUse)).ToArray ();
1846 foreach (var n in relevant.Where (n => n.DoesNotHaveApiStyle (styleToUse))) {
1847 if (valueMatches (n)) {
1851 n.AddApiStyle (ApiStyle.Classic);
1852 addedOldApiStyle = true;
1857 if (!existing.Any ()) {
1858 var newNode = makeNewNode ();
1859 if (shouldDuplicate && addedOldApiStyle) {
1860 newNode.AddApiStyle (ApiStyle.Unified);
1864 var itemToReuse = existing.First ();
1865 setValue (itemToReuse);
1867 if (shouldDuplicate && addedOldApiStyle) {
1868 itemToReuse.AddApiStyle (styleToUse);
1874 static readonly string[] MemberNodeOrder = {
1889 static void OrderMemberNodes (XmlNode member, XmlNodeList children)
1891 ReorderNodes (member, children, MemberNodeOrder);
1894 static void ReorderNodes (XmlNode node, XmlNodeList children, string[] ordering)
1896 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1897 for (int i = 0; i < ordering.Length; ++i) {
1898 for (int j = 0; j < children.Count; ++j) {
1899 XmlNode c = children [j];
1900 if (c.Name == ordering [i]) {
1901 newChildren.Add (c);
1905 if (newChildren.Count >= 0)
1906 node.PrependChild ((XmlNode) newChildren [0]);
1907 for (int i = 1; i < newChildren.Count; ++i) {
1908 XmlNode prev = (XmlNode) newChildren [i-1];
1909 XmlNode cur = (XmlNode) newChildren [i];
1910 node.RemoveChild (cur);
1911 node.InsertAfter (cur, prev);
1915 IEnumerable<string> GetCustomAttributes (MemberReference mi)
1917 IEnumerable<string> attrs = Enumerable.Empty<string>();
1919 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1921 attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1923 PropertyDefinition pd = mi as PropertyDefinition;
1925 if (pd.GetMethod != null)
1926 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1927 if (pd.SetMethod != null)
1928 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1931 EventDefinition ed = mi as EventDefinition;
1933 if (ed.AddMethod != null)
1934 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1935 if (ed.RemoveMethod != null)
1936 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1942 IEnumerable<string> GetCustomAttributes (IList<CustomAttribute> attributes, string prefix)
1944 foreach (CustomAttribute attribute in attributes.OrderBy (ca => ca.AttributeType.FullName)) {
1946 TypeDefinition attrType = attribute.AttributeType as TypeDefinition;
1947 if (attrType != null && !IsPublic (attrType))
1949 if (slashdocFormatter.GetName (attribute.AttributeType) == null)
1952 if (Array.IndexOf (IgnorableAttributes, attribute.AttributeType.FullName) >= 0)
1955 StringList fields = new StringList ();
1957 for (int i = 0; i < attribute.ConstructorArguments.Count; ++i) {
1958 CustomAttributeArgument argument = attribute.ConstructorArguments [i];
1959 fields.Add (MakeAttributesValueString (
1964 (from namedArg in attribute.Fields
1965 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value })
1967 (from namedArg in attribute.Properties
1968 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value }))
1969 .OrderBy (v => v.Name);
1970 foreach (var d in namedArgs)
1971 fields.Add (string.Format ("{0}={1}", d.Name,
1972 MakeAttributesValueString (d.Value, d.Type)));
1974 string a2 = String.Join(", ", fields.ToArray ());
1975 if (a2 != "") a2 = "(" + a2 + ")";
1977 string name = attribute.GetDeclaringType();
1978 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1979 yield return prefix + name + a2;
1983 static readonly string[] ValidExtensionMembers = {
1992 static readonly string[] ValidExtensionDocMembers = {
1998 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
2000 MethodDefinition me = info.Member as MethodDefinition;
2003 if (info.Parameters.Count < 1)
2005 if (!DocUtils.IsExtensionMethod (me))
2008 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
2009 XmlNode member = e.CloneNode (true);
2010 em.AppendChild (member);
2011 RemoveExcept (member, ValidExtensionMembers);
2012 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
2013 WriteElementText (member, "MemberType", "ExtensionMethod");
2014 XmlElement link = member.OwnerDocument.CreateElement ("Link");
2015 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
2016 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
2017 member.AppendChild (link);
2018 AddTargets (em, info);
2020 var sig = em.SelectSingleNode ("Member/MemberSignature[@Language='C#']/@Value");
2021 if (!IsMultiAssembly || (IsMultiAssembly && sig != null && !extensionMethods.Any (ex => ex.SelectSingleNode ("Member/MemberSignature[@Language='C#']/@Value").Value == sig.Value))) {
2022 extensionMethods.Add (em);
2026 private static void RemoveExcept (XmlNode node, string[] except)
2030 MyXmlNodeList remove = null;
2031 foreach (XmlNode n in node.ChildNodes) {
2032 if (Array.BinarySearch (except, n.Name) < 0) {
2034 remove = new MyXmlNodeList ();
2039 foreach (XmlNode n in remove)
2040 node.RemoveChild (n);
2043 private static void AddTargets (XmlNode member, DocsNodeInfo info)
2045 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
2046 member.PrependChild (targets);
2047 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
2048 AppendElementAttributeText (targets, "Target", "Type",
2049 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
2052 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
2053 IList<TypeReference> constraints = gp.Constraints;
2054 if (constraints.Count == 0)
2055 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
2057 foreach (TypeReference c in constraints)
2058 AppendElementAttributeText(targets, "Target", "Type",
2059 slashdocFormatter.GetDeclaration (c));
2063 private static bool GetFieldConstValue (FieldDefinition field, out string value)
2066 TypeDefinition type = field.DeclaringType.Resolve ();
2067 if (type != null && type.IsEnum) return false;
2069 if (type != null && type.IsGenericType ()) return false;
2070 if (!field.HasConstant)
2072 if (field.IsLiteral) {
2073 object val = field.Constant;
2074 if (val == null) value = "null";
2075 else if (val is Enum) value = val.ToString();
2076 else if (val is IFormattable) {
2077 value = ((IFormattable)val).ToString(null, CultureInfo.InvariantCulture);
2079 value = "\"" + value + "\"";
2081 if (value != null && value != "")
2087 // XML HELPER FUNCTIONS
2089 internal static XmlElement WriteElement(XmlNode parent, string element, bool forceNewElement = false) {
2090 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
2091 if (ret == null || forceNewElement) {
2092 string[] path = element.Split('/');
2093 foreach (string p in path) {
2094 ret = (XmlElement)parent.SelectSingleNode(p);
2095 if (ret == null || forceNewElement) {
2097 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
2098 ename = ename.Substring(0, ename.IndexOf('['));
2099 ret = parent.OwnerDocument.CreateElement(ename);
2100 parent.AppendChild(ret);
2109 private static XmlElement WriteElementText(XmlNode parent, string element, string value, bool forceNewElement = false) {
2110 XmlElement node = WriteElement(parent, element, forceNewElement: forceNewElement);
2111 node.InnerText = value;
2115 static XmlElement AppendElementText (XmlNode parent, string element, string value)
2117 XmlElement n = parent.OwnerDocument.CreateElement (element);
2118 parent.AppendChild (n);
2119 n.InnerText = value;
2123 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
2125 XmlElement n = parent.OwnerDocument.CreateElement (element);
2126 parent.AppendChild (n);
2127 n.SetAttribute (attribute, value);
2131 internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
2133 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
2134 dest.AppendChild (copy);
2138 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
2139 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
2142 node = WriteElement(parent, element);
2143 node.InnerText = value;
2145 private static XmlElement WriteElementAttribute(XmlElement parent, string element, string attribute, string value, bool forceNewElement = false) {
2146 XmlElement node = WriteElement(parent, element, forceNewElement:forceNewElement);
2147 return WriteElementAttribute (parent, node, attribute, value);
2149 private static XmlElement WriteElementAttribute(XmlElement parent, XmlElement node, string attribute, string value) {
2150 if (node.GetAttribute (attribute) != value) {
2151 node.SetAttribute (attribute, value);
2155 internal static void ClearElement(XmlElement parent, string name) {
2156 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
2158 parent.RemoveChild(node);
2161 // DOCUMENTATION HELPER FUNCTIONS
2163 private void MakeDocNode (DocsNodeInfo info)
2165 List<GenericParameter> genericParams = info.GenericParameters;
2166 IList<ParameterDefinition> parameters = info.Parameters;
2167 TypeReference returntype = info.ReturnType;
2168 bool returnisreturn = info.ReturnIsReturn;
2169 XmlElement e = info.Node;
2170 bool addremarks = info.AddRemarks;
2172 WriteElementInitialText(e, "summary", "To be added.");
2174 if (parameters != null) {
2175 string[] values = new string [parameters.Count];
2176 for (int i = 0; i < values.Length; ++i)
2177 values [i] = parameters [i].Name;
2178 UpdateParameters (e, "param", values);
2181 if (genericParams != null) {
2182 string[] values = new string [genericParams.Count];
2183 for (int i = 0; i < values.Length; ++i)
2184 values [i] = genericParams [i].Name;
2185 UpdateParameters (e, "typeparam", values);
2188 string retnodename = null;
2189 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
2190 retnodename = returnisreturn ? "returns" : "value";
2191 string retnodename_other = !returnisreturn ? "returns" : "value";
2193 // If it has a returns node instead of a value node, change its name.
2194 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
2195 if (retother != null) {
2196 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
2197 foreach (XmlNode node in retother)
2198 retnode.AppendChild(node.CloneNode(true));
2199 e.ReplaceChild(retnode, retother);
2201 WriteElementInitialText(e, retnodename, "To be added.");
2204 ClearElement(e, "returns");
2205 ClearElement(e, "value");
2209 WriteElementInitialText(e, "remarks", "To be added.");
2211 if (exceptions.HasValue && info.Member != null &&
2212 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
2213 UpdateExceptions (e, info.Member);
2216 foreach (DocumentationImporter importer in importers)
2217 importer.ImportDocumentation (info);
2219 OrderDocsNodes (e, e.ChildNodes);
2220 NormalizeWhitespace(e);
2223 static readonly string[] DocsNodeOrder = {
2224 "typeparam", "param", "summary", "returns", "value", "remarks",
2227 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
2229 ReorderNodes (docs, children, DocsNodeOrder);
2233 private void UpdateParameters (XmlElement e, string element, string[] values)
2235 if (values != null) {
2236 XmlNode[] paramnodes = new XmlNode[values.Length];
2238 // Some documentation had param nodes with leading spaces.
2239 foreach (XmlElement paramnode in e.SelectNodes(element)){
2240 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
2243 // If a member has only one parameter, we can track changes to
2244 // the name of the parameter easily.
2245 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
2246 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
2249 bool reinsert = false;
2251 // Pick out existing and still-valid param nodes, and
2252 // create nodes for parameters not in the file.
2253 Hashtable seenParams = new Hashtable();
2254 for (int pi = 0; pi < values.Length; pi++) {
2255 string p = values [pi];
2258 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
2259 if (paramnodes[pi] != null) continue;
2261 XmlElement pe = e.OwnerDocument.CreateElement(element);
2262 pe.SetAttribute("name", p);
2263 pe.InnerText = "To be added.";
2264 paramnodes[pi] = pe;
2268 // Remove parameters that no longer exist and check all params are in the right order.
2270 MyXmlNodeList todelete = new MyXmlNodeList ();
2271 foreach (XmlElement paramnode in e.SelectNodes(element)) {
2272 string name = paramnode.GetAttribute("name");
2273 if (!seenParams.ContainsKey(name)) {
2274 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2275 Warning ("The following param node can only be deleted if the --delete option is given: ");
2276 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
2278 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
2279 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2283 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
2284 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2285 e.ParentNode.Attributes ["MemberName"].Value,
2288 Warning ("\tValue={0}", paramnode.OuterXml);
2290 todelete.Add (paramnode);
2295 if ((int)seenParams[name] != idx)
2301 foreach (XmlNode n in todelete) {
2302 n.ParentNode.RemoveChild (n);
2305 // Re-insert the parameter nodes at the top of the doc section.
2307 for (int pi = values.Length-1; pi >= 0; pi--)
2308 e.PrependChild(paramnodes[pi]);
2310 // Clear all existing param nodes
2311 foreach (XmlNode paramnode in e.SelectNodes(element)) {
2312 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2313 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
2314 Console.WriteLine(paramnode.OuterXml);
2316 paramnode.ParentNode.RemoveChild(paramnode);
2322 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
2324 string existingName = pe.GetAttribute ("name");
2325 pe.SetAttribute ("name", newName);
2326 if (existingName == newName)
2328 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
2329 if (paramref.GetAttribute ("name").Trim () == existingName)
2330 paramref.SetAttribute ("name", newName);
2333 class CrefComparer : XmlNodeComparer {
2335 public CrefComparer ()
2339 public override int Compare (XmlNode x, XmlNode y)
2341 string xType = x.Attributes ["cref"].Value;
2342 string yType = y.Attributes ["cref"].Value;
2343 string xNamespace = GetNamespace (xType);
2344 string yNamespace = GetNamespace (yType);
2346 int c = xNamespace.CompareTo (yNamespace);
2349 return xType.CompareTo (yType);
2352 static string GetNamespace (string type)
2354 int n = type.LastIndexOf ('.');
2356 return type.Substring (0, n);
2357 return string.Empty;
2361 private void UpdateExceptions (XmlNode docs, MemberReference member)
2363 string indent = new string (' ', 10);
2364 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
2365 string cref = slashdocFormatter.GetDeclaration (source.Exception);
2366 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
2369 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
2370 e.SetAttribute ("cref", cref);
2371 e.InnerXml = "To be added; from:\n" + indent + "<see cref=\"" +
2372 string.Join ("\" />,\n" + indent + "<see cref=\"",
2373 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
2374 .OrderBy (s => s)) +
2376 docs.AppendChild (e);
2378 SortXmlNodes (docs, docs.SelectNodes ("exception"),
2379 new CrefComparer ());
2382 private static void NormalizeWhitespace(XmlElement e) {
2383 // Remove all text and whitespace nodes from the element so it
2384 // is outputted with nice indentation and no blank lines.
2385 ArrayList deleteNodes = new ArrayList();
2386 foreach (XmlNode n in e)
2387 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2389 foreach (XmlNode n in deleteNodes)
2390 n.ParentNode.RemoveChild(n);
2393 private bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
2395 TypeDefinition type = member as TypeDefinition;
2397 type = member.DeclaringType as TypeDefinition;
2399 var versions = new string[] { GetAssemblyVersion (type.Module.Assembly) };
2401 if (root.LocalName == "AssemblyInfo")
2402 return UpdateAssemblyVersionForAssemblyInfo (root, root.ParentNode as XmlElement, versions, add: true);
2404 return UpdateAssemblyVersions (root, type.Module.Assembly, versions, add);
2407 private static string GetAssemblyVersion (AssemblyDefinition assembly)
2409 return assembly.Name.Version.ToString();
2412 private bool UpdateAssemblyVersions(XmlElement root, AssemblyDefinition assembly, string[] assemblyVersions, bool add)
2414 if (IsMultiAssembly)
2417 XmlElement av = (XmlElement) root.SelectSingleNode ("AssemblyVersions");
2419 // AssemblyVersions is not part of the spec
2420 root.RemoveChild (av);
2423 string oldNodeFilter = "AssemblyInfo[not(@apistyle) or @apistyle='classic']";
2424 string newNodeFilter = "AssemblyInfo[@apistyle='unified']";
2425 string thisNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? newNodeFilter : oldNodeFilter;
2426 string thatNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? oldNodeFilter : newNodeFilter;
2428 XmlElement e = (XmlElement) root.SelectSingleNode (thisNodeFilter);
2430 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2432 if (MDocUpdater.HasDroppedNamespace (assembly)) {
2433 e.AddApiStyle (ApiStyle.Unified);
2436 root.AppendChild(e);
2439 var thatNode = (XmlElement) root.SelectSingleNode (thatNodeFilter);
2440 if (MDocUpdater.HasDroppedNamespace (assembly) && thatNode != null) {
2441 // there's a classic node, we should add apistyles
2442 e.AddApiStyle (ApiStyle.Unified);
2443 thatNode.AddApiStyle (ApiStyle.Classic);
2446 return UpdateAssemblyVersionForAssemblyInfo (e, root, assemblyVersions, add);
2449 static bool UpdateAssemblyVersionForAssemblyInfo (XmlElement e, XmlElement root, string[] assemblyVersions, bool add)
2451 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().Where (v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0).ToList ();
2452 // matches.Count > 0 && add: ignore -- already present
2453 if (matches.Count > 0 && !add) {
2454 foreach (XmlNode c in matches)
2457 else if (matches.Count == 0 && add) {
2458 foreach (string sv in assemblyVersions) {
2459 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2465 // matches.Count == 0 && !add: ignore -- already not present
2466 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2467 SortXmlNodes (e, avs, new VersionComparer ());
2469 bool anyNodesLeft = avs.Count != 0;
2470 if (!anyNodesLeft) {
2471 e.ParentNode.RemoveChild (e);
2473 return anyNodesLeft;
2476 // FIXME: get TypeReferences instead of string comparison?
2477 private static string[] IgnorableAttributes = {
2478 // Security related attributes
2479 "System.Reflection.AssemblyKeyFileAttribute",
2480 "System.Reflection.AssemblyDelaySignAttribute",
2481 // Present in @RefType
2482 "System.Runtime.InteropServices.OutAttribute",
2483 // For naming the indexer to use when not using indexers
2484 "System.Reflection.DefaultMemberAttribute",
2485 // for decimal constants
2486 "System.Runtime.CompilerServices.DecimalConstantAttribute",
2487 // compiler generated code
2488 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
2489 // more compiler generated code, e.g. iterator methods
2490 "System.Diagnostics.DebuggerHiddenAttribute",
2491 "System.Runtime.CompilerServices.FixedBufferAttribute",
2492 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
2493 // extension methods
2494 "System.Runtime.CompilerServices.ExtensionAttribute",
2495 // Used to differentiate 'object' from C#4 'dynamic'
2496 "System.Runtime.CompilerServices.DynamicAttribute",
2499 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes, TypeReference t=null)
2501 if (!attributes.Any ()) {
2502 ClearElement (root, "Attributes");
2506 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2510 e = root.OwnerDocument.CreateElement("Attributes");
2512 foreach (string attribute in attributes) {
2513 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2516 WriteElementText(ae, "AttributeName", attribute);
2519 if (e.ParentNode == null)
2520 root.AppendChild(e);
2522 NormalizeWhitespace(e);
2525 public static string MakeAttributesValueString (object v, TypeReference valueType)
2527 var formatters = new [] {
2528 new AttributeValueFormatter (),
2529 new ApplePlatformEnumFormatter (),
2530 new StandardFlagsEnumFormatter (),
2531 new DefaultAttributeValueFormatter (),
2534 ResolvedTypeInfo type = new ResolvedTypeInfo (valueType);
2535 foreach (var formatter in formatters) {
2536 string formattedValue;
2537 if (formatter.TryFormatValue (v, type, out formattedValue)) {
2538 return formattedValue;
2542 // this should never occur because the DefaultAttributeValueFormatter will always
2543 // successfully format the value ... but this is needed to satisfy the compiler :)
2544 throw new InvalidDataException (string.Format ("Unable to format attribute value ({0})", v.ToString ()));
2547 internal static IDictionary<long, string> GetEnumerationValues (TypeDefinition type)
2549 var values = new Dictionary<long, string> ();
2551 (from f in type.Fields
2552 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
2554 values [ToInt64 (f.Constant)] = f.Name;
2559 internal static long ToInt64 (object value)
2562 return (long) (ulong) value;
2563 return Convert.ToInt64 (value);
2566 private void MakeParameters (XmlElement root, MemberReference member, IList<ParameterDefinition> parameters, bool shouldDuplicateWithNew=false)
2568 XmlElement e = WriteElement(root, "Parameters");
2571 foreach (ParameterDefinition p in parameters) {
2575 var ptype = GetDocParameterType (p.ParameterType);
2576 var newPType = ptype;
2578 if (MDocUpdater.SwitchingToMagicTypes) {
2579 newPType = NativeTypeManager.ConvertFromNativeType (ptype);
2582 // now find the existing node, if it's there so we can reuse it.
2583 var nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2584 .Cast<XmlElement> ().Where (x => x.GetAttribute ("Name") == p.Name)
2587 if (nodes.Count () == 0) {
2588 // wasn't found, let's make sure it wasn't just cause the param name was changed
2589 nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2590 .Cast<XmlElement> ()
2591 .Skip (i) // this makes sure we don't inadvertently "reuse" nodes when adding new ones
2592 .Where (x => x.GetAttribute ("Name") != p.Name && (x.GetAttribute ("Type") == ptype || x.GetAttribute ("Type") == newPType))
2593 .Take(1) // there might be more than one that meets this parameter ... only take the first.
2598 x => x.GetAttribute ("Type") == ptype,
2599 x => x.SetAttribute ("Type", ptype),
2601 pe = root.OwnerDocument.CreateElement ("Parameter");
2604 pe.SetAttribute ("Name", p.Name);
2605 pe.SetAttribute ("Type", ptype);
2606 if (p.ParameterType is ByReferenceType) {
2608 pe.SetAttribute ("RefType", "out");
2610 pe.SetAttribute ("RefType", "ref");
2613 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
2622 private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams, MemberReference member, bool shouldDuplicateWithNew)
2624 if (typeParams == null || typeParams.Count == 0) {
2625 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2627 root.RemoveChild (f);
2630 XmlElement e = WriteElement(root, "TypeParameters");
2632 var nodes = e.SelectNodes ("TypeParameter").Cast<XmlElement> ().ToArray ();
2634 foreach (GenericParameter t in typeParams) {
2636 IList<TypeReference> constraints = t.Constraints;
2637 GenericParameterAttributes attrs = t.Attributes;
2643 var baseType = e.SelectSingleNode("BaseTypeName");
2644 // TODO: should this comparison take into account BaseTypeName?
2645 return x.GetAttribute("Name") == t.Name;
2647 x => {}, // no additional action required
2650 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2652 pe.SetAttribute("Name", t.Name);
2653 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""), t.DeclaringType);
2654 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2655 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2663 ce = root.OwnerDocument.CreateElement ("Constraints");
2665 pe.AppendChild (ce);
2666 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2667 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2668 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2669 AppendElementText (ce, "ParameterAttribute", "Covariant");
2670 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2671 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2672 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2673 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2674 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2675 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2676 foreach (TypeReference c in constraints) {
2677 TypeDefinition cd = c.Resolve ();
2678 AppendElementText (ce,
2679 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2680 GetDocTypeFullName (c));
2689 private void MakeParameters (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew)
2691 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2692 MakeParameters (root, mi, ((MethodDefinition)mi).Parameters, shouldDuplicateWithNew);
2693 else if (mi is MethodDefinition) {
2694 MethodDefinition mb = (MethodDefinition) mi;
2695 IList<ParameterDefinition> parameters = mb.Parameters;
2696 MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
2697 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2698 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2699 p.SetAttribute ("RefType", "this");
2702 else if (mi is PropertyDefinition) {
2703 IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
2704 if (parameters.Count > 0)
2705 MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
2709 else if (mi is FieldDefinition) return;
2710 else if (mi is EventDefinition) return;
2711 else throw new ArgumentException();
2714 internal static string GetDocParameterType (TypeReference type)
2716 return GetDocTypeFullName (type).Replace ("@", "&");
2719 private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes, bool shouldDuplicateWithNew=false)
2721 XmlElement e = WriteElement(root, "ReturnValue");
2722 var valueToUse = GetDocTypeFullName (type);
2724 AddXmlNode (e.SelectNodes("ReturnType").Cast<XmlElement> ().ToArray (),
2725 x => x.InnerText == valueToUse,
2726 x => x.InnerText = valueToUse,
2728 var newNode = WriteElementText(e, "ReturnType", valueToUse, forceNewElement: true);
2729 if (attributes != null)
2730 MakeAttributes(e, GetCustomAttributes (attributes, ""), type);
2737 private void MakeReturnValue (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew=false)
2739 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2741 else if (mi is MethodDefinition)
2742 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes, shouldDuplicateWithNew);
2743 else if (mi is PropertyDefinition)
2744 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null, shouldDuplicateWithNew);
2745 else if (mi is FieldDefinition)
2746 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null, shouldDuplicateWithNew);
2747 else if (mi is EventDefinition)
2748 MakeReturnValue (root, ((EventDefinition)mi).EventType, null, shouldDuplicateWithNew);
2750 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2753 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2755 MemberReference mi = info.Member;
2756 if (mi is TypeDefinition) return null;
2758 string sigs = memberFormatters [0].GetDeclaration (mi);
2759 if (sigs == null) return null; // not publicly visible
2761 // no documentation for property/event accessors. Is there a better way of doing this?
2762 if (mi.Name.StartsWith("get_")) return null;
2763 if (mi.Name.StartsWith("set_")) return null;
2764 if (mi.Name.StartsWith("add_")) return null;
2765 if (mi.Name.StartsWith("remove_")) return null;
2766 if (mi.Name.StartsWith("raise_")) return null;
2768 XmlElement me = doc.CreateElement("Member");
2769 me.SetAttribute("MemberName", GetMemberName (mi));
2773 if (exceptions.HasValue &&
2774 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2775 UpdateExceptions (info.Node, info.Member);
2777 if (since != null) {
2778 XmlNode docs = me.SelectSingleNode("Docs");
2779 docs.AppendChild (CreateSinceNode (doc));
2785 internal static string GetMemberName (MemberReference mi)
2787 MethodDefinition mb = mi as MethodDefinition;
2789 PropertyDefinition pi = mi as PropertyDefinition;
2792 return DocUtils.GetPropertyName (pi);
2794 StringBuilder sb = new StringBuilder (mi.Name.Length);
2795 if (!DocUtils.IsExplicitlyImplemented (mb))
2796 sb.Append (mi.Name);
2798 TypeReference iface;
2799 MethodReference ifaceMethod;
2800 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2801 sb.Append (GetDocTypeFullName (iface));
2803 sb.Append (ifaceMethod.Name);
2805 if (mb.IsGenericMethod ()) {
2806 IList<GenericParameter> typeParams = mb.GenericParameters;
2807 if (typeParams.Count > 0) {
2809 sb.Append (typeParams [0].Name);
2810 for (int i = 1; i < typeParams.Count; ++i)
2811 sb.Append (",").Append (typeParams [i].Name);
2815 return sb.ToString ();
2818 /// SIGNATURE GENERATION FUNCTIONS
2819 internal static bool IsPrivate (MemberReference mi)
2821 return memberFormatters [0].GetDeclaration (mi) == null;
2824 internal static string GetMemberType (MemberReference mi)
2826 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2827 return "Constructor";
2828 if (mi is MethodDefinition)
2830 if (mi is PropertyDefinition)
2832 if (mi is FieldDefinition)
2834 if (mi is EventDefinition)
2836 throw new ArgumentException();
2839 private static string GetDocTypeName (TypeReference type)
2841 return docTypeFormatter.GetName (type);
2844 internal static string GetDocTypeFullName (TypeReference type)
2846 return DocTypeFullMemberFormatter.Default.GetName (type);
2849 internal static string GetXPathForMember (DocumentationMember member)
2851 StringBuilder xpath = new StringBuilder ();
2852 xpath.Append ("//Members/Member[@MemberName=\"")
2853 .Append (member.MemberName)
2855 if (member.Parameters != null && member.Parameters.Count > 0) {
2856 xpath.Append ("/Parameters[count(Parameter) = ")
2857 .Append (member.Parameters.Count);
2858 for (int i = 0; i < member.Parameters.Count; ++i) {
2859 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2860 xpath.Append (member.Parameters [i]);
2861 xpath.Append ("\"");
2863 xpath.Append ("]/..");
2865 return xpath.ToString ();
2868 public static string GetXPathForMember (XPathNavigator member)
2870 StringBuilder xpath = new StringBuilder ();
2871 xpath.Append ("//Type[@FullName=\"")
2872 .Append (member.SelectSingleNode ("../../@FullName").Value)
2874 xpath.Append ("Members/Member[@MemberName=\"")
2875 .Append (member.SelectSingleNode ("@MemberName").Value)
2877 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2878 if (parameters.Count > 0) {
2879 xpath.Append ("/Parameters[count(Parameter) = ")
2880 .Append (parameters.Count);
2882 while (parameters.MoveNext ()) {
2884 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2885 xpath.Append (parameters.Current.Value);
2886 xpath.Append ("\"");
2888 xpath.Append ("]/..");
2890 return xpath.ToString ();
2893 public static string GetXPathForMember (MemberReference member)
2895 StringBuilder xpath = new StringBuilder ();
2896 xpath.Append ("//Type[@FullName=\"")
2897 .Append (member.DeclaringType.FullName)
2899 xpath.Append ("Members/Member[@MemberName=\"")
2900 .Append (GetMemberName (member))
2903 IList<ParameterDefinition> parameters = null;
2904 if (member is MethodDefinition)
2905 parameters = ((MethodDefinition) member).Parameters;
2906 else if (member is PropertyDefinition) {
2907 parameters = ((PropertyDefinition) member).Parameters;
2909 if (parameters != null && parameters.Count > 0) {
2910 xpath.Append ("/Parameters[count(Parameter) = ")
2911 .Append (parameters.Count);
2912 for (int i = 0; i < parameters.Count; ++i) {
2913 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2914 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2915 xpath.Append ("\"");
2917 xpath.Append ("]/..");
2919 return xpath.ToString ();
2923 static class CecilExtensions {
2924 public static string GetDeclaringType(this CustomAttribute attribute)
2926 var type = attribute.Constructor.DeclaringType;
2927 var typeName = type.FullName;
2929 string translatedType = NativeTypeManager.GetTranslatedName (type);
2930 return translatedType;
2933 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
2935 foreach (var c in type.Methods.Where (m => m.IsConstructor))
2936 yield return (MemberReference) c;
2937 foreach (var e in type.Events)
2938 yield return (MemberReference) e;
2939 foreach (var f in type.Fields)
2940 yield return (MemberReference) f;
2941 foreach (var m in type.Methods.Where (m => !m.IsConstructor))
2942 yield return (MemberReference) m;
2943 foreach (var t in type.NestedTypes)
2944 yield return (MemberReference) t;
2945 foreach (var p in type.Properties)
2946 yield return (MemberReference) p;
2949 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
2951 return GetMembers (type).Where (m => m.Name == member);
2954 public static MemberReference GetMember (this TypeDefinition type, string member)
2956 return GetMembers (type, member).EnsureZeroOrOne ();
2959 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2961 if (source.Count () > 1)
2962 throw new InvalidOperationException ("too many matches");
2963 return source.FirstOrDefault ();
2966 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2969 .Where (m => m.Name == method)
2970 .EnsureZeroOrOne ();
2973 public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
2975 TypeDefinition def = type as TypeDefinition;
2977 return new MemberReference [0];
2978 CustomAttribute defMemberAttr = def.CustomAttributes
2979 .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
2980 if (defMemberAttr == null)
2981 return new MemberReference [0];
2982 string name = (string) defMemberAttr.ConstructorArguments [0].Value;
2983 return def.Properties
2984 .Where (p => p.Name == name)
2985 .Select (p => (MemberReference) p);
2988 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2990 return assembly.Modules.SelectMany (md => md.GetAllTypes ());
2993 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2995 return GetTypes (assembly)
2996 .Where (td => td.FullName == type)
2997 .EnsureZeroOrOne ();
3000 public static bool IsGenericType (this TypeReference type)
3002 return type.GenericParameters.Count > 0;
3005 public static bool IsGenericMethod (this MethodReference method)
3007 return method.GenericParameters.Count > 0;
3010 public static MemberReference Resolve (this MemberReference member)
3012 FieldReference fr = member as FieldReference;
3014 return fr.Resolve ();
3015 MethodReference mr = member as MethodReference;
3017 return mr.Resolve ();
3018 TypeReference tr = member as TypeReference;
3020 return tr.Resolve ();
3021 PropertyReference pr = member as PropertyReference;
3024 EventReference er = member as EventReference;
3027 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
3030 public static TypeReference GetUnderlyingType (this TypeDefinition type)
3034 return type.Fields.First (f => f.Name == "value__").FieldType;
3037 public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
3039 return self.Types.SelectMany (t => t.GetAllTypes ());
3042 static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
3046 if (!self.HasNestedTypes)
3049 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
3059 static class DocUtils {
3061 public static bool DoesNotHaveApiStyle(this XmlElement element, ApiStyle style) {
3062 string styleString = style.ToString ().ToLowerInvariant ();
3063 string apistylevalue = element.GetAttribute ("apistyle");
3064 return apistylevalue != styleString || string.IsNullOrWhiteSpace(apistylevalue);
3066 public static bool HasApiStyle(this XmlElement element, ApiStyle style) {
3067 string styleString = style.ToString ().ToLowerInvariant ();
3068 return element.GetAttribute ("apistyle") == styleString;
3070 public static bool HasApiStyle(this XmlNode node, ApiStyle style)
3072 var attribute = node.Attributes ["apistyle"];
3073 return attribute != null && attribute.Value == style.ToString ().ToLowerInvariant ();
3075 public static void AddApiStyle(this XmlElement element, ApiStyle style) {
3076 string styleString = style.ToString ().ToLowerInvariant ();
3077 var existingValue = element.GetAttribute ("apistyle");
3078 if (string.IsNullOrWhiteSpace (existingValue) || existingValue != styleString) {
3079 element.SetAttribute ("apistyle", styleString);
3082 // Propagate the API style up to the membernode if necessary
3083 if (element.LocalName == "AssemblyInfo" && element.ParentNode != null && element.ParentNode.LocalName == "Member") {
3084 var member = element.ParentNode;
3085 var unifiedAssemblyNode = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']");
3086 var classicAssemblyNode = member.SelectSingleNode ("AssemblyInfo[not(@apistyle) or @apistyle='classic']");
3088 var parentAttribute = element.ParentNode.Attributes ["apistyle"];
3089 Action removeStyle = () => element.ParentNode.Attributes.Remove (parentAttribute);
3090 Action propagateStyle = () => {
3091 if (parentAttribute == null) {
3092 // if it doesn't have the attribute, then add it
3093 parentAttribute = element.OwnerDocument.CreateAttribute ("apistyle");
3094 parentAttribute.Value = styleString;
3095 element.ParentNode.Attributes.Append (parentAttribute);
3099 if ((style == ApiStyle.Classic && unifiedAssemblyNode != null) || (style == ApiStyle.Unified && classicAssemblyNode != null))
3105 public static void AddApiStyle (this XmlNode node, ApiStyle style)
3107 string styleString = style.ToString ().ToLowerInvariant ();
3108 var existingAttribute = node.Attributes ["apistyle"];
3109 if (existingAttribute == null) {
3110 existingAttribute = node.OwnerDocument.CreateAttribute ("apistyle");
3111 node.Attributes.Append (existingAttribute);
3113 existingAttribute.Value = styleString;
3115 public static void RemoveApiStyle (this XmlElement element, ApiStyle style)
3117 string styleString = style.ToString ().ToLowerInvariant ();
3118 string existingValue = element.GetAttribute ("apistyle");
3119 if (string.IsNullOrWhiteSpace (existingValue) || existingValue == styleString) {
3120 element.RemoveAttribute ("apistyle");
3123 public static void RemoveApiStyle (this XmlNode node, ApiStyle style)
3125 var styleAttribute = node.Attributes ["apistyle"];
3126 if (styleAttribute != null && styleAttribute.Value == style.ToString ().ToLowerInvariant ()) {
3127 node.Attributes.Remove (styleAttribute);
3131 public static bool IsExplicitlyImplemented (MethodDefinition method)
3133 return method.IsPrivate && method.IsFinal && method.IsVirtual;
3136 public static string GetTypeDotMember (string name)
3138 int startType, startMethod;
3139 startType = startMethod = -1;
3140 for (int i = 0; i < name.Length; ++i) {
3141 if (name [i] == '.') {
3142 startType = startMethod;
3146 return name.Substring (startType+1);
3149 public static string GetMember (string name)
3151 int i = name.LastIndexOf ('.');
3154 return name.Substring (i+1);
3157 public static void GetInfoForExplicitlyImplementedMethod (
3158 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
3162 if (method.Overrides.Count != 1)
3163 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
3164 iface = method.Overrides [0].DeclaringType;
3165 ifaceMethod = method.Overrides [0];
3168 public static string GetPropertyName (PropertyDefinition pi)
3170 // Issue: (g)mcs-generated assemblies that explicitly implement
3171 // properties don't specify the full namespace, just the
3172 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
3173 MethodDefinition method = pi.GetMethod;
3175 method = pi.SetMethod;
3176 if (!IsExplicitlyImplemented (method))
3179 // Need to determine appropriate namespace for this member.
3180 TypeReference iface;
3181 MethodReference ifaceMethod;
3182 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3183 return string.Join (".", new string[]{
3184 DocTypeFullMemberFormatter.Default.GetName (iface),
3185 GetMember (pi.Name)});
3188 public static string GetNamespace (TypeReference type)
3190 if (type.GetElementType ().IsNested)
3191 type = type.GetElementType ();
3192 while (type != null && type.IsNested)
3193 type = type.DeclaringType;
3195 return string.Empty;
3197 string typeNS = type.Namespace;
3199 // first, make sure this isn't a type reference to another assembly/module
3201 bool isInAssembly = MDocUpdater.IsInAssemblies(type.Module.Name);
3202 if (isInAssembly && !typeNS.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (type)) {
3203 typeNS = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typeNS);
3208 public static string PathCombine (string dir, string path)
3214 return Path.Combine (dir, path);
3217 public static bool IsExtensionMethod (MethodDefinition method)
3220 method.CustomAttributes
3221 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
3222 && method.DeclaringType.CustomAttributes
3223 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
3226 public static bool IsDelegate (TypeDefinition type)
3228 TypeReference baseRef = type.BaseType;
3229 if (baseRef == null)
3231 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
3232 baseRef.FullName == "System.MulticastDelegate";
3235 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
3237 List<TypeReference> decls = new List<TypeReference> ();
3239 while (type.DeclaringType != null) {
3240 decls.Add (type.DeclaringType);
3241 type = type.DeclaringType;
3247 public static int GetGenericArgumentCount (TypeReference type)
3249 GenericInstanceType inst = type as GenericInstanceType;
3251 ? inst.GenericArguments.Count
3252 : type.GenericParameters.Count;
3255 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
3257 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
3258 List<TypeReference> userInterfaces = new List<TypeReference> ();
3259 foreach (TypeReference iface in type.Interfaces) {
3260 TypeReference lookup = iface.Resolve () ?? iface;
3261 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
3262 userInterfaces.Add (iface);
3264 return userInterfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()));
3267 private static string GetQualifiedTypeName (TypeReference type)
3269 return "[" + type.Scope.Name + "]" + type.FullName;
3272 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
3274 HashSet<string> inheritedInterfaces = new HashSet<string> ();
3275 Action<TypeDefinition> a = null;
3277 if (t == null) return;
3278 foreach (TypeReference r in t.Interfaces) {
3279 inheritedInterfaces.Add (GetQualifiedTypeName (r));
3283 TypeReference baseRef = type.BaseType;
3284 while (baseRef != null) {
3285 TypeDefinition baseDef = baseRef.Resolve ();
3286 if (baseDef != null) {
3288 baseRef = baseDef.BaseType;
3293 foreach (TypeReference r in type.Interfaces)
3295 return inheritedInterfaces;
3299 class DocsNodeInfo {
3300 public DocsNodeInfo (XmlElement node)
3305 public DocsNodeInfo (XmlElement node, TypeDefinition type)
3311 public DocsNodeInfo (XmlElement node, MemberReference member)
3314 SetMemberInfo (member);
3317 void SetType (TypeDefinition type)
3320 throw new ArgumentNullException ("type");
3322 GenericParameters = new List<GenericParameter> (type.GenericParameters);
3323 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
3324 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
3325 for (int i = 0; i < declTypes.Count - 1; ++i) {
3326 int remove = System.Math.Min (maxGenArgs,
3327 DocUtils.GetGenericArgumentCount (declTypes [i]));
3328 maxGenArgs -= remove;
3329 while (remove-- > 0)
3330 GenericParameters.RemoveAt (0);
3332 if (DocUtils.IsDelegate (type)) {
3333 Parameters = type.GetMethod("Invoke").Parameters;
3334 ReturnType = type.GetMethod("Invoke").ReturnType;
3335 ReturnIsReturn = true;
3339 void SetMemberInfo (MemberReference member)
3342 throw new ArgumentNullException ("member");
3343 ReturnIsReturn = true;
3347 if (member is MethodReference ) {
3348 MethodReference mr = (MethodReference) member;
3349 Parameters = mr.Parameters;
3350 if (mr.IsGenericMethod ()) {
3351 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
3354 else if (member is PropertyDefinition) {
3355 Parameters = ((PropertyDefinition) member).Parameters;
3358 if (member is MethodDefinition) {
3359 ReturnType = ((MethodDefinition) member).ReturnType;
3360 } else if (member is PropertyDefinition) {
3361 ReturnType = ((PropertyDefinition) member).PropertyType;
3362 ReturnIsReturn = false;
3365 // no remarks section for enum members
3366 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
3370 public TypeReference ReturnType;
3371 public List<GenericParameter> GenericParameters;
3372 public IList<ParameterDefinition> Parameters;
3373 public bool ReturnIsReturn;
3374 public XmlElement Node;
3375 public bool AddRemarks = true;
3376 public MemberReference Member;
3377 public TypeDefinition Type;
3379 public override string ToString ()
3381 return string.Format ("{0} - {1} - {2}", Type, Member, Node == null ? "no xml" : "with xml");
3385 class DocumentationEnumerator {
3387 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3389 return GetDocumentationTypes (assembly, forTypes, null);
3392 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3394 foreach (TypeDefinition type in assembly.GetTypes()) {
3395 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
3397 if (seen != null && seen.Contains (type.FullName))
3400 foreach (TypeDefinition nested in type.NestedTypes)
3401 yield return nested;
3405 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3407 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
3408 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
3409 oldmember.RemoveAttribute ("__monodocer-seen__");
3412 MemberReference m = GetMember (type, new DocumentationMember (oldmember));
3414 yield return new DocsNodeInfo (oldmember);
3417 yield return new DocsNodeInfo (oldmember, m);
3422 protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
3424 string membertype = member.MemberType;
3426 string returntype = member.ReturnType;
3428 string docName = member.MemberName;
3430 string[] docTypeParams = GetTypeParameters (docName, member.TypeParameters);
3432 // If we're using 'magic types', then we might get false positives ... in those cases, we keep searching
3433 MemberReference likelyCandidate = null;
3435 // Loop through all members in this type with the same name
3436 var reflectedMembers = GetReflectionMembers (type, docName).ToArray ();
3437 foreach (MemberReference mi in reflectedMembers) {
3438 bool matchedMagicType = false;
3439 if (mi is TypeDefinition) continue;
3440 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
3442 if (MDocUpdater.IsPrivate (mi))
3445 IList<ParameterDefinition> pis = null;
3446 string[] typeParams = null;
3447 if (mi is MethodDefinition) {
3448 MethodDefinition mb = (MethodDefinition) mi;
3449 pis = mb.Parameters;
3450 if (mb.IsGenericMethod ()) {
3451 IList<GenericParameter> args = mb.GenericParameters;
3452 typeParams = args.Select (p => p.Name).ToArray ();
3455 else if (mi is PropertyDefinition)
3456 pis = ((PropertyDefinition)mi).Parameters;
3458 // check type parameters
3459 int methodTcount = member.TypeParameters == null ? 0 : member.TypeParameters.Count;
3460 int reflectionTcount = typeParams == null ? 0 : typeParams.Length;
3461 if (methodTcount != reflectionTcount)
3464 // check member parameters
3465 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
3466 int pcount = pis == null ? 0 : pis.Count;
3467 if (mcount != pcount)
3470 MethodDefinition mDef = mi as MethodDefinition;
3471 if (mDef != null && !mDef.IsConstructor) {
3472 // Casting operators can overload based on return type.
3473 string rtype = GetReplacedString (
3474 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
3475 typeParams, docTypeParams);
3476 string originalRType = rtype;
3477 if (MDocUpdater.SwitchingToMagicTypes) {
3478 rtype = NativeTypeManager.ConvertFromNativeType (rtype);
3481 if ((returntype != rtype && originalRType == rtype) ||
3482 (MDocUpdater.SwitchingToMagicTypes && returntype != originalRType && returntype != rtype && originalRType != rtype)) {
3486 if (originalRType != rtype)
3487 matchedMagicType = true;
3493 for (int i = 0; i < pis.Count; i++) {
3494 string paramType = GetReplacedString (
3495 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
3496 typeParams, docTypeParams);
3498 // if magictypes, replace paramType to "classic value" ... so the comparison works
3499 string originalParamType = paramType;
3500 if (MDocUpdater.SwitchingToMagicTypes) {
3501 paramType = NativeTypeManager.ConvertFromNativeType (paramType);
3504 string xmlMemberType = member.Parameters [i];
3505 if ((!paramType.Equals(xmlMemberType) && paramType.Equals(originalParamType)) ||
3506 (MDocUpdater.SwitchingToMagicTypes && !originalParamType.Equals(xmlMemberType) && !paramType.Equals(xmlMemberType) && !paramType.Equals(originalParamType))) {
3508 // did not match ... if we're dropping the namespace, and the paramType has the dropped
3509 // namespace, we should see if it matches when added
3510 bool stillDoesntMatch = true;
3511 if (MDocUpdater.HasDroppedNamespace(type) && paramType.StartsWith (MDocUpdater.droppedNamespace)) {
3512 string withDroppedNs = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, xmlMemberType);
3514 stillDoesntMatch = withDroppedNs != paramType;
3517 if (stillDoesntMatch) {
3523 if (originalParamType != paramType)
3524 matchedMagicType = true;
3526 if (!good) continue;
3528 if (MDocUpdater.SwitchingToMagicTypes && likelyCandidate == null && matchedMagicType) {
3529 // 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
3530 likelyCandidate = mi;
3537 return likelyCandidate;
3540 static string[] GetTypeParameters (string docName, IEnumerable<string> knownParameters)
3542 if (docName [docName.Length-1] != '>')
3544 StringList types = new StringList ();
3545 int endToken = docName.Length-2;
3546 int i = docName.Length-2;
3548 if (docName [i] == ',' || docName [i] == '<') {
3549 types.Add (docName.Substring (i + 1, endToken - i));
3552 if (docName [i] == '<')
3557 var arrayTypes = types.ToArray ();
3559 if (knownParameters != null && knownParameters.Any () && arrayTypes.Length != knownParameters.Count ())
3560 return knownParameters.ToArray ();
3565 protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
3567 // In case of dropping the namespace, we have to remove the dropped NS
3568 // so that docName will match what's in the assembly/type
3569 if (MDocUpdater.HasDroppedNamespace (type) && docName.StartsWith(MDocUpdater.droppedNamespace + ".")) {
3570 int droppedNsLength = MDocUpdater.droppedNamespace.Length;
3571 docName = docName.Substring (droppedNsLength + 1, docName.Length - droppedNsLength - 1);
3574 // need to worry about 4 forms of //@MemberName values:
3575 // 1. "Normal" (non-generic) member names: GetEnumerator
3577 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
3578 // - try as-is, and try type.member (due to "kludge" for property
3580 // 3. "Normal" Generic member names: Sort<T> (CSC)
3581 // - need to remove generic parameters --> "Sort"
3582 // 4. Explicitly-implemented interface members for generic interfaces:
3583 // -- System.Collections.Generic.IEnumerable<T>.Current
3584 // - Try as-is, and try type.member, *keeping* the generic parameters.
3585 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
3586 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
3587 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
3588 // this as (1) or (2).
3589 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
3591 foreach (MemberReference mi in type.GetMembers (docName))
3593 if (CountChars (docName, '.') > 0)
3594 // might be a property; try only type.member instead of
3595 // namespace.type.member.
3596 foreach (MemberReference mi in
3597 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
3604 int startLt, startType, startMethod;
3605 startLt = startType = startMethod = -1;
3606 for (int i = 0; i < docName.Length; ++i) {
3607 switch (docName [i]) {
3616 if (numLt == 0 && (i + 1) < docName.Length)
3617 // there's another character in docName, so this <...> sequence is
3618 // probably part of a generic type -- case 4.
3622 startType = startMethod;
3628 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
3630 foreach (MemberReference mi in type.GetMembers (refName))
3634 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
3637 // If we _still_ haven't found it, we've hit another generic naming issue:
3638 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
3639 // explicitly-implemented METHOD names (not properties), e.g.
3640 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
3641 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
3642 // which the XML docs will contain.
3644 // Alas, we can't derive the Mono name from docName, so we need to iterate
3645 // over all member names, convert them into CSC format, and compare... :-(
3648 foreach (MemberReference mi in type.GetMembers ()) {
3649 if (MDocUpdater.GetMemberName (mi) == docName)
3654 static string GetReplacedString (string typeName, string[] from, string[] to)
3658 for (int i = 0; i < from.Length; ++i)
3659 typeName = typeName.Replace (from [i], to [i]);
3663 private static int CountChars (string s, char c)
3666 for (int i = 0; i < s.Length; ++i) {
3674 class EcmaDocumentationEnumerator : DocumentationEnumerator {
3679 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
3682 this.ecmadocs = ecmaDocs;
3685 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3687 HashSet<string> seen = new HashSet<string> ();
3688 return GetDocumentationTypes (assembly, forTypes, seen)
3689 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
3692 new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3695 while (ecmadocs.Read ()) {
3696 switch (ecmadocs.Name) {
3698 if (typeDepth == -1)
3699 typeDepth = ecmadocs.Depth;
3700 if (ecmadocs.NodeType != XmlNodeType.Element)
3702 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
3704 string typename = ecmadocs.GetAttribute ("FullName");
3705 string typename2 = MDocUpdater.GetTypeFileName (typename);
3706 if (forTypes != null &&
3707 forTypes.BinarySearch (typename) < 0 &&
3708 typename != typename2 &&
3709 forTypes.BinarySearch (typename2) < 0)
3712 if ((t = assembly.GetType (typename)) == null &&
3713 (t = assembly.GetType (typename2)) == null)
3715 seen.Add (typename);
3716 if (typename != typename2)
3717 seen.Add (typename2);
3718 Console.WriteLine (" Import: {0}", t.FullName);
3719 if (ecmadocs.Name != "Docs") {
3720 int depth = ecmadocs.Depth;
3721 while (ecmadocs.Read ()) {
3722 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
3726 if (!ecmadocs.IsStartElement ("Docs"))
3727 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
3737 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3739 return GetMembers (basefile, type)
3740 .Concat (base.GetDocumentationMembers (basefile, type));
3743 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
3745 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
3748 if (ecmadocs.IsEmptyElement)
3751 int membersDepth = ecmadocs.Depth;
3753 while (go && ecmadocs.Read ()) {
3754 switch (ecmadocs.Name) {
3756 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
3758 DocumentationMember dm = new DocumentationMember (ecmadocs);
3760 string xp = MDocUpdater.GetXPathForMember (dm);
3761 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
3763 if (oldmember == null) {
3764 m = GetMember (type, dm);
3766 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3767 type.FullName, dm.MemberSignatures ["C#"]);
3768 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
3771 // oldmember lookup may have failed due to type parameter renames.
3773 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
3774 if (oldmember == null) {
3775 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
3776 oldmember = basefile.CreateElement ("Member");
3777 oldmember.SetAttribute ("MemberName", dm.MemberName);
3778 members.AppendChild (oldmember);
3779 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
3780 XmlElement ms = basefile.CreateElement ("MemberSignature");
3781 ms.SetAttribute ("Language", key);
3782 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
3783 oldmember.AppendChild (ms);
3785 oldmember.SetAttribute ("__monodocer-seen__", "true");
3786 Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
3791 m = GetMember (type, new DocumentationMember (oldmember));
3793 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3794 type.FullName, dm.MemberSignatures ["C#"]);
3797 oldmember.SetAttribute ("__monodocer-seen__", "true");
3799 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
3800 if (ecmadocs.Name != "Docs")
3801 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
3806 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
3815 abstract class DocumentationImporter {
3817 public abstract void ImportDocumentation (DocsNodeInfo info);
3820 class MsxdocDocumentationImporter : DocumentationImporter {
3822 XmlDocument slashdocs;
3824 public MsxdocDocumentationImporter (string file)
3826 var xml = File.ReadAllText (file);
3828 // Ensure Unix line endings
3829 xml = xml.Replace ("\r", "");
3831 slashdocs = new XmlDocument();
3832 slashdocs.LoadXml (xml);
3835 public override void ImportDocumentation (DocsNodeInfo info)
3837 XmlNode elem = GetDocs (info.Member ?? info.Type);
3842 XmlElement e = info.Node;
3844 if (elem.SelectSingleNode("summary") != null)
3845 MDocUpdater.ClearElement(e, "summary");
3846 if (elem.SelectSingleNode("remarks") != null)
3847 MDocUpdater.ClearElement(e, "remarks");
3848 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3849 MDocUpdater.ClearElement(e, "value");
3850 MDocUpdater.ClearElement(e, "returns");
3853 foreach (XmlNode child in elem.ChildNodes) {
3854 switch (child.Name) {
3857 XmlAttribute name = child.Attributes ["name"];
3860 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3862 p2.InnerXml = child.InnerXml;
3865 // Occasionally XML documentation will use <returns/> on
3866 // properties, so let's try to normalize things.
3869 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3870 v.InnerXml = child.InnerXml;
3876 case "permission": {
3877 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3880 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3882 a = e.OwnerDocument.CreateElement (child.Name);
3883 a.SetAttribute ("cref", cref.Value);
3886 a.InnerXml = child.InnerXml;
3890 XmlAttribute cref = child.Attributes ["cref"];
3893 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3895 a = e.OwnerDocument.CreateElement ("altmember");
3896 a.SetAttribute ("cref", cref.Value);
3903 if (child.NodeType == XmlNodeType.Element &&
3904 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3907 MDocUpdater.CopyNode (child, e);
3914 private XmlNode GetDocs (MemberReference member)
3916 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3917 if (slashdocsig != null)
3918 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3923 class EcmaDocumentationImporter : DocumentationImporter {
3927 public EcmaDocumentationImporter (XmlReader ecmaDocs)
3929 this.ecmadocs = ecmaDocs;
3932 public override void ImportDocumentation (DocsNodeInfo info)
3934 if (!ecmadocs.IsStartElement ("Docs")) {
3938 XmlElement e = info.Node;
3940 int depth = ecmadocs.Depth;
3941 ecmadocs.ReadStartElement ("Docs");
3942 while (ecmadocs.Read ()) {
3943 if (ecmadocs.Name == "Docs") {
3944 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3947 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3949 if (!ecmadocs.IsStartElement ())
3951 switch (ecmadocs.Name) {
3954 string name = ecmadocs.GetAttribute ("name");
3957 XmlNode doc = e.SelectSingleNode (
3958 ecmadocs.Name + "[@name='" + name + "']");
3959 string value = ecmadocs.ReadInnerXml ();
3961 doc.InnerXml = value.Replace ("\r", "");
3968 string name = ecmadocs.Name;
3969 string cref = ecmadocs.GetAttribute ("cref");
3972 XmlNode doc = e.SelectSingleNode (
3973 ecmadocs.Name + "[@cref='" + cref + "']");
3974 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3976 doc.InnerXml = value;
3978 XmlElement n = e.OwnerDocument.CreateElement (name);
3979 n.SetAttribute ("cref", cref);
3986 string name = ecmadocs.Name;
3987 string xpath = ecmadocs.Name;
3988 StringList attributes = new StringList (ecmadocs.AttributeCount);
3989 if (ecmadocs.MoveToFirstAttribute ()) {
3991 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3992 } while (ecmadocs.MoveToNextAttribute ());
3993 ecmadocs.MoveToContent ();
3995 if (attributes.Count > 0) {
3996 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3998 XmlNode doc = e.SelectSingleNode (xpath);
3999 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
4001 doc.InnerXml = value;
4004 XmlElement n = e.OwnerDocument.CreateElement (name);
4006 foreach (string a in attributes) {
4007 int eq = a.IndexOf ('=');
4008 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
4019 class DocumentationMember {
4020 public StringToStringMap MemberSignatures = new StringToStringMap ();
4021 public string ReturnType;
4022 public StringList Parameters;
4023 public StringList TypeParameters;
4024 public string MemberName;
4025 public string MemberType;
4027 public DocumentationMember (XmlReader reader)
4029 MemberName = reader.GetAttribute ("MemberName");
4030 int depth = reader.Depth;
4032 StringList p = new StringList ();
4033 StringList tp = new StringList ();
4035 if (reader.NodeType != XmlNodeType.Element)
4038 bool shouldUse = true;
4040 string apistyle = reader.GetAttribute ("apistyle");
4041 shouldUse = string.IsNullOrWhiteSpace(apistyle) || apistyle == "classic"; // only use this tag if it's an 'classic' style node
4043 catch (Exception ex) {}
4044 switch (reader.Name) {
4045 case "MemberSignature":
4047 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
4051 MemberType = reader.ReadElementString ();
4054 if (reader.Depth == depth + 2 && shouldUse)
4055 ReturnType = reader.ReadElementString ();
4058 if (reader.Depth == depth + 2 && shouldUse)
4059 p.Add (reader.GetAttribute ("Type"));
4061 case "TypeParameter":
4062 if (reader.Depth == depth + 2 && shouldUse)
4063 tp.Add (reader.GetAttribute ("Name"));
4066 if (reader.Depth == depth + 1)
4070 } while (go && reader.Read () && reader.Depth >= depth);
4075 TypeParameters = tp;
4077 DiscernTypeParameters ();
4081 public DocumentationMember (XmlNode node)
4083 MemberName = node.Attributes ["MemberName"].Value;
4084 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
4085 XmlAttribute l = n.Attributes ["Language"];
4086 XmlAttribute v = n.Attributes ["Value"];
4087 XmlAttribute apistyle = n.Attributes ["apistyle"];
4088 bool shouldUse = apistyle == null || apistyle.Value == "classic";
4089 if (l != null && v != null && shouldUse)
4090 MemberSignatures [l.Value] = v.Value;
4092 MemberType = node.SelectSingleNode ("MemberType").InnerText;
4093 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType[not(@apistyle) or @apistyle='classic']");
4095 ReturnType = rt.InnerText;
4096 XmlNodeList p = node.SelectNodes ("Parameters/Parameter[not(@apistyle) or @apistyle='classic']");
4098 Parameters = new StringList (p.Count);
4099 for (int i = 0; i < p.Count; ++i)
4100 Parameters.Add (p [i].Attributes ["Type"].Value);
4102 XmlNodeList tp = node.SelectNodes ("TypeParameters/TypeParameter[not(@apistyle) or @apistyle='classic']");
4104 TypeParameters = new StringList (tp.Count);
4105 for (int i = 0; i < tp.Count; ++i)
4106 TypeParameters.Add (tp [i].Attributes ["Name"].Value);
4109 DiscernTypeParameters ();
4113 void DiscernTypeParameters ()
4115 // see if we can discern the param list from the name
4116 if (MemberName.Contains ("<") && MemberName.EndsWith (">")) {
4117 var starti = MemberName.IndexOf ("<") + 1;
4118 var endi = MemberName.LastIndexOf (">");
4119 var paramlist = MemberName.Substring (starti, endi - starti);
4120 var tparams = paramlist.Split (new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
4121 TypeParameters = new StringList (tparams);
4126 public class DynamicParserContext {
4127 public ReadOnlyCollection<bool> TransformFlags;
4128 public int TransformIndex;
4130 public DynamicParserContext (ICustomAttributeProvider provider)
4133 if (provider.HasCustomAttributes &&
4134 (da = (provider.CustomAttributes.Cast<CustomAttribute>()
4135 .SingleOrDefault (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))) != null) {
4136 CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
4137 ? new CustomAttributeArgument [0]
4138 : (CustomAttributeArgument[]) da.ConstructorArguments [0].Value;
4140 TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool) t.Value).ToArray());
4145 public enum MemberFormatterState {
4147 WithinGenericTypeParameters,
4150 public abstract class MemberFormatter {
4152 public virtual string Language {
4156 public string GetName (MemberReference member)
4158 return GetName (member, null);
4161 public virtual string GetName (MemberReference member, DynamicParserContext context)
4163 TypeReference type = member as TypeReference;
4165 return GetTypeName (type, context);
4166 MethodReference method = member as MethodReference;
4167 if (method != null && method.Name == ".ctor") // method.IsConstructor
4168 return GetConstructorName (method);
4170 return GetMethodName (method);
4171 PropertyReference prop = member as PropertyReference;
4173 return GetPropertyName (prop);
4174 FieldReference field = member as FieldReference;
4176 return GetFieldName (field);
4177 EventReference e = member as EventReference;
4179 return GetEventName (e);
4180 throw new NotSupportedException ("Can't handle: " +
4181 (member == null ? "null" : member.GetType().ToString()));
4184 protected virtual string GetTypeName (TypeReference type)
4186 return GetTypeName (type, null);
4189 protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
4192 throw new ArgumentNullException ("type");
4193 return _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
4196 protected virtual char[] ArrayDelimeters {
4197 get {return new char[]{'[', ']'};}
4200 protected virtual MemberFormatterState MemberFormatterState { get; set; }
4202 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4204 if (type is ArrayType) {
4205 TypeSpecification spec = type as TypeSpecification;
4206 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
4207 return AppendArrayModifiers (buf, (ArrayType) type);
4209 if (type is ByReferenceType) {
4210 return AppendRefTypeName (buf, type, context);
4212 if (type is PointerType) {
4213 return AppendPointerTypeName (buf, type, context);
4215 if (type is GenericParameter) {
4216 return AppendTypeName (buf, type, context);
4218 AppendNamespace (buf, type);
4219 GenericInstanceType genInst = type as GenericInstanceType;
4220 if (type.GenericParameters.Count == 0 &&
4221 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
4222 return AppendFullTypeName (buf, type, context);
4224 return AppendGenericType (buf, type, context);
4227 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4229 string ns = DocUtils.GetNamespace (type);
4230 if (ns != null && ns.Length > 0)
4231 buf.Append (ns).Append ('.');
4235 protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4237 if (type.DeclaringType != null)
4238 AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
4239 return AppendTypeName (buf, type, context);
4242 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4244 if (context != null)
4245 context.TransformIndex++;
4246 return AppendTypeName (buf, type.Name);
4249 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
4251 int n = typename.IndexOf ("`");
4253 return buf.Append (typename.Substring (0, n));
4254 return buf.Append (typename);
4257 protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
4259 buf.Append (ArrayDelimeters [0]);
4260 int rank = array.Rank;
4262 buf.Append (new string (',', rank-1));
4263 return buf.Append (ArrayDelimeters [1]);
4266 protected virtual string RefTypeModifier {
4270 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4272 TypeSpecification spec = type as TypeSpecification;
4273 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4274 .Append (RefTypeModifier);
4277 protected virtual string PointerModifier {
4281 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4283 TypeSpecification spec = type as TypeSpecification;
4284 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4285 .Append (PointerModifier);
4288 protected virtual char[] GenericTypeContainer {
4289 get {return new char[]{'<', '>'};}
4292 protected virtual char NestedTypeSeparator {
4296 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4298 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4299 type is GenericInstanceType ? type.GetElementType () : type);
4300 List<TypeReference> genArgs = GetGenericArguments (type);
4303 bool insertNested = false;
4304 foreach (var decl in decls) {
4305 TypeReference declDef = decl.Resolve () ?? decl;
4307 buf.Append (NestedTypeSeparator);
4309 insertNested = true;
4310 AppendTypeName (buf, declDef, context);
4311 int ac = DocUtils.GetGenericArgumentCount (declDef);
4315 buf.Append (GenericTypeContainer [0]);
4316 var origState = MemberFormatterState;
4317 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4318 _AppendTypeName (buf, genArgs [argIdx++], context);
4319 for (int i = 1; i < c; ++i) {
4320 _AppendTypeName (buf.Append (","), genArgs [argIdx++], context);
4322 MemberFormatterState = origState;
4323 buf.Append (GenericTypeContainer [1]);
4329 protected List<TypeReference> GetGenericArguments (TypeReference type)
4331 var args = new List<TypeReference> ();
4332 GenericInstanceType inst = type as GenericInstanceType;
4334 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
4336 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
4340 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4345 protected virtual string GetConstructorName (MethodReference constructor)
4347 return constructor.Name;
4350 protected virtual string GetMethodName (MethodReference method)
4355 protected virtual string GetPropertyName (PropertyReference property)
4357 return property.Name;
4360 protected virtual string GetFieldName (FieldReference field)
4365 protected virtual string GetEventName (EventReference e)
4370 public virtual string GetDeclaration (MemberReference member)
4373 throw new ArgumentNullException ("member");
4374 TypeDefinition type = member as TypeDefinition;
4376 return GetTypeDeclaration (type);
4377 MethodDefinition method = member as MethodDefinition;
4378 if (method != null && method.IsConstructor)
4379 return GetConstructorDeclaration (method);
4381 return GetMethodDeclaration (method);
4382 PropertyDefinition prop = member as PropertyDefinition;
4384 return GetPropertyDeclaration (prop);
4385 FieldDefinition field = member as FieldDefinition;
4387 return GetFieldDeclaration (field);
4388 EventDefinition e = member as EventDefinition;
4390 return GetEventDeclaration (e);
4391 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
4394 protected virtual string GetTypeDeclaration (TypeDefinition type)
4397 throw new ArgumentNullException ("type");
4398 StringBuilder buf = new StringBuilder (type.Name.Length);
4399 _AppendTypeName (buf, type, null);
4400 AppendGenericTypeConstraints (buf, type);
4401 return buf.ToString ();
4404 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
4406 return GetConstructorName (constructor);
4409 protected virtual string GetMethodDeclaration (MethodDefinition method)
4411 if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
4412 ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
4415 // Special signature for destructors.
4416 if (method.Name == "Finalize" && method.Parameters.Count == 0)
4417 return GetFinalizerName (method);
4419 StringBuilder buf = new StringBuilder ();
4421 AppendVisibility (buf, method);
4422 if (buf.Length == 0 &&
4423 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
4426 AppendModifiers (buf, method);
4428 if (buf.Length != 0)
4430 buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
4432 AppendMethodName (buf, method);
4433 AppendGenericMethod (buf, method).Append (" ");
4434 AppendParameters (buf, method, method.Parameters);
4435 AppendGenericMethodConstraints (buf, method);
4436 return buf.ToString ();
4439 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4441 return buf.Append (method.Name);
4444 protected virtual string GetFinalizerName (MethodDefinition method)
4449 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4454 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4459 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4464 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4469 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4474 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
4476 return GetPropertyName (property);
4479 protected virtual string GetFieldDeclaration (FieldDefinition field)
4481 return GetFieldName (field);
4484 protected virtual string GetEventDeclaration (EventDefinition e)
4486 return GetEventName (e);
4490 class ILFullMemberFormatter : MemberFormatter {
4492 public override string Language {
4493 get {return "ILAsm";}
4496 protected override char NestedTypeSeparator {
4502 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4504 if (GetBuiltinType (type.FullName) != null)
4506 string ns = DocUtils.GetNamespace (type);
4507 if (ns != null && ns.Length > 0) {
4508 if (type.IsValueType)
4509 buf.Append ("valuetype ");
4511 buf.Append ("class ");
4512 buf.Append (ns).Append ('.');
4517 protected static string GetBuiltinType (string t)
4520 case "System.Byte": return "unsigned int8";
4521 case "System.SByte": return "int8";
4522 case "System.Int16": return "int16";
4523 case "System.Int32": return "int32";
4524 case "System.Int64": return "int64";
4525 case "System.IntPtr": return "native int";
4527 case "System.UInt16": return "unsigned int16";
4528 case "System.UInt32": return "unsigned int32";
4529 case "System.UInt64": return "unsigned int64";
4530 case "System.UIntPtr": return "native unsigned int";
4532 case "System.Single": return "float32";
4533 case "System.Double": return "float64";
4534 case "System.Boolean": return "bool";
4535 case "System.Char": return "char";
4536 case "System.Void": return "void";
4537 case "System.String": return "string";
4538 case "System.Object": return "object";
4543 protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
4545 return buf.Append (typename);
4548 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4550 if (type is GenericParameter) {
4551 AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
4555 string s = GetBuiltinType (type.FullName);
4557 return buf.Append (s);
4559 return base.AppendTypeName (buf, type, context);
4562 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
4564 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
4565 return buf.Append (type.Owner is TypeReference ? "!" : "!!");
4567 GenericParameterAttributes attrs = type.Attributes;
4568 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
4569 buf.Append ("class ");
4570 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
4571 buf.Append ("struct ");
4572 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
4573 buf.Append (".ctor ");
4574 IList<TypeReference> constraints = type.Constraints;
4575 MemberFormatterState = 0;
4576 if (constraints.Count > 0) {
4577 var full = new ILFullMemberFormatter ();
4578 buf.Append ("(").Append (full.GetName (constraints [0]));
4579 for (int i = 1; i < constraints.Count; ++i) {
4580 buf.Append (", ").Append (full.GetName (constraints [i]));
4584 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4586 if ((attrs & GenericParameterAttributes.Covariant) != 0)
4588 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
4593 protected override string GetTypeDeclaration (TypeDefinition type)
4595 string visibility = GetTypeVisibility (type.Attributes);
4596 if (visibility == null)
4599 StringBuilder buf = new StringBuilder ();
4601 buf.Append (".class ");
4603 buf.Append ("nested ");
4604 buf.Append (visibility).Append (" ");
4605 if (type.IsInterface)
4606 buf.Append ("interface ");
4607 if (type.IsSequentialLayout)
4608 buf.Append ("sequential ");
4609 if (type.IsAutoLayout)
4610 buf.Append ("auto ");
4611 if (type.IsAnsiClass)
4612 buf.Append ("ansi ");
4613 if (type.IsAbstract)
4614 buf.Append ("abstract ");
4615 if (type.IsSerializable)
4616 buf.Append ("serializable ");
4618 buf.Append ("sealed ");
4619 if (type.IsBeforeFieldInit)
4620 buf.Append ("beforefieldinit ");
4621 var state = MemberFormatterState;
4622 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4623 buf.Append (GetName (type));
4624 MemberFormatterState = state;
4625 var full = new ILFullMemberFormatter ();
4626 if (type.BaseType != null) {
4627 buf.Append (" extends ");
4628 if (type.BaseType.FullName == "System.Object")
4629 buf.Append ("System.Object");
4631 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
4634 foreach (var name in type.Interfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()))
4635 .Select (i => full.GetName (i))
4636 .OrderBy (n => n)) {
4638 buf.Append (" implements ");
4647 return buf.ToString ();
4650 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4652 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4653 type is GenericInstanceType ? type.GetElementType () : type);
4655 foreach (var decl in decls) {
4656 TypeReference declDef = decl.Resolve () ?? decl;
4658 buf.Append (NestedTypeSeparator);
4661 AppendTypeName (buf, declDef, context);
4665 foreach (TypeReference arg in GetGenericArguments (type)) {
4669 _AppendTypeName (buf, arg, context);
4675 static string GetTypeVisibility (TypeAttributes ta)
4677 switch (ta & TypeAttributes.VisibilityMask) {
4678 case TypeAttributes.Public:
4679 case TypeAttributes.NestedPublic:
4682 case TypeAttributes.NestedFamily:
4683 case TypeAttributes.NestedFamORAssem:
4691 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4693 return GetMethodDeclaration (constructor);
4696 protected override string GetMethodDeclaration (MethodDefinition method)
4698 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
4701 var buf = new StringBuilder ();
4702 buf.Append (".method ");
4703 AppendVisibility (buf, method);
4704 if (method.IsStatic)
4705 buf.Append ("static ");
4706 if (method.IsHideBySig)
4707 buf.Append ("hidebysig ");
4708 if (method.IsPInvokeImpl) {
4709 var info = method.PInvokeInfo;
4710 buf.Append ("pinvokeimpl (\"")
4711 .Append (info.Module.Name)
4712 .Append ("\" as \"")
4713 .Append (info.EntryPoint)
4715 if (info.IsCharSetAuto)
4716 buf.Append (" auto");
4717 if (info.IsCharSetUnicode)
4718 buf.Append (" unicode");
4719 if (info.IsCharSetAnsi)
4720 buf.Append (" ansi");
4721 if (info.IsCallConvCdecl)
4722 buf.Append (" cdecl");
4723 if (info.IsCallConvStdCall)
4724 buf.Append (" stdcall");
4725 if (info.IsCallConvWinapi)
4726 buf.Append (" winapi");
4727 if (info.IsCallConvThiscall)
4728 buf.Append (" thiscall");
4729 if (info.SupportsLastError)
4730 buf.Append (" lasterr");
4733 if (method.IsSpecialName)
4734 buf.Append ("specialname ");
4735 if (method.IsRuntimeSpecialName)
4736 buf.Append ("rtspecialname ");
4737 if (method.IsNewSlot)
4738 buf.Append ("newslot ");
4739 if (method.IsVirtual)
4740 buf.Append ("virtual ");
4741 if (!method.IsStatic)
4742 buf.Append ("instance ");
4743 _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
4745 .Append (method.Name);
4746 if (method.IsGenericMethod ()) {
4747 var state = MemberFormatterState;
4748 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4749 IList<GenericParameter> args = method.GenericParameters;
4750 if (args.Count > 0) {
4752 _AppendTypeName (buf, args [0], null);
4753 for (int i = 1; i < args.Count; ++i)
4754 _AppendTypeName (buf.Append (", "), args [i], null);
4757 MemberFormatterState = state;
4762 for (int i = 0; i < method.Parameters.Count; ++i) {
4766 _AppendTypeName (buf, method.Parameters [i].ParameterType, new DynamicParserContext (method.Parameters [i]));
4768 buf.Append (method.Parameters [i].Name);
4772 buf.Append (" cil");
4773 if (method.IsRuntime)
4774 buf.Append (" runtime");
4775 if (method.IsManaged)
4776 buf.Append (" managed");
4778 return buf.ToString ();
4781 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4783 if (DocUtils.IsExplicitlyImplemented (method)) {
4784 TypeReference iface;
4785 MethodReference ifaceMethod;
4786 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4787 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4789 .Append (ifaceMethod.Name);
4791 return base.AppendMethodName (buf, method);
4794 protected override string RefTypeModifier {
4798 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4800 if (method.IsPublic)
4801 return buf.Append ("public ");
4802 if (method.IsFamilyAndAssembly)
4803 return buf.Append ("familyandassembly");
4804 if (method.IsFamilyOrAssembly)
4805 return buf.Append ("familyorassembly");
4806 if (method.IsFamily)
4807 return buf.Append ("family");
4811 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4813 string modifiers = String.Empty;
4814 if (method.IsStatic) modifiers += " static";
4815 if (method.IsVirtual && !method.IsAbstract) {
4816 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4817 else modifiers += " override";
4819 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4820 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4821 if (method.IsFinal) modifiers += " sealed";
4822 if (modifiers == " virtual sealed") modifiers = "";
4824 return buf.Append (modifiers);
4827 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4829 if (method.IsGenericMethod ()) {
4830 IList<GenericParameter> args = method.GenericParameters;
4831 if (args.Count > 0) {
4833 buf.Append (args [0].Name);
4834 for (int i = 1; i < args.Count; ++i)
4835 buf.Append (",").Append (args [i].Name);
4842 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4844 return AppendParameters (buf, method, parameters, '(', ')');
4847 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4851 if (parameters.Count > 0) {
4852 if (DocUtils.IsExtensionMethod (method))
4853 buf.Append ("this ");
4854 AppendParameter (buf, parameters [0]);
4855 for (int i = 1; i < parameters.Count; ++i) {
4857 AppendParameter (buf, parameters [i]);
4861 return buf.Append (end);
4864 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4866 if (parameter.ParameterType is ByReferenceType) {
4867 if (parameter.IsOut)
4868 buf.Append ("out ");
4870 buf.Append ("ref ");
4872 buf.Append (GetName (parameter.ParameterType)).Append (" ");
4873 return buf.Append (parameter.Name);
4876 protected override string GetPropertyDeclaration (PropertyDefinition property)
4878 MethodDefinition gm = null, sm = null;
4880 string get_visible = null;
4881 if ((gm = property.GetMethod) != null &&
4882 (DocUtils.IsExplicitlyImplemented (gm) ||
4883 (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
4884 get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
4885 string set_visible = null;
4886 if ((sm = property.SetMethod) != null &&
4887 (DocUtils.IsExplicitlyImplemented (sm) ||
4888 (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
4889 set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
4891 if ((set_visible == null) && (get_visible == null))
4894 StringBuilder buf = new StringBuilder ()
4895 .Append (".property ");
4896 if (!(gm ?? sm).IsStatic)
4897 buf.Append ("instance ");
4898 _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
4899 buf.Append (' ').Append (property.Name);
4900 if (!property.HasParameters || property.Parameters.Count == 0)
4901 return buf.ToString ();
4905 foreach (ParameterDefinition p in property.Parameters) {
4909 _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
4913 return buf.ToString ();
4916 protected override string GetFieldDeclaration (FieldDefinition field)
4918 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4919 if (declType.IsEnum && field.Name == "value__")
4920 return null; // This member of enums aren't documented.
4922 StringBuilder buf = new StringBuilder ();
4923 AppendFieldVisibility (buf, field);
4924 if (buf.Length == 0)
4927 buf.Insert (0, ".field ");
4930 buf.Append ("static ");
4931 if (field.IsInitOnly)
4932 buf.Append ("initonly ");
4933 if (field.IsLiteral)
4934 buf.Append ("literal ");
4935 _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
4936 buf.Append (' ').Append (field.Name);
4937 AppendFieldValue (buf, field);
4939 return buf.ToString ();
4942 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4945 return buf.Append ("public ");
4946 if (field.IsFamilyAndAssembly)
4947 return buf.Append ("familyandassembly ");
4948 if (field.IsFamilyOrAssembly)
4949 return buf.Append ("familyorassembly ");
4951 return buf.Append ("family ");
4955 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4957 // enums have a value__ field, which we ignore
4958 if (field.DeclaringType.IsGenericType ())
4960 if (field.HasConstant && field.IsLiteral) {
4963 val = field.Constant;
4968 buf.Append (" = ").Append ("null");
4969 else if (val is Enum)
4971 .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4973 .Append (val.ToString ())
4975 else if (val is IFormattable) {
4976 string value = ((IFormattable)val).ToString(null, CultureInfo.InvariantCulture);
4979 buf.Append ("\"" + value + "\"");
4981 buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4990 protected override string GetEventDeclaration (EventDefinition e)
4992 StringBuilder buf = new StringBuilder ();
4993 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4998 buf.Append (".event ")
4999 .Append (GetName (e.EventType))
5003 return buf.ToString ();
5007 class ILMemberFormatter : ILFullMemberFormatter {
5008 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5014 class ILNativeTypeMemberFormatter : ILFullMemberFormatter {
5015 protected static string _GetBuiltinType (string t)
5017 //string moddedType = base.GetBuiltinType (t);
5019 //return moddedType;
5023 class CSharpNativeTypeMemberFormatter : CSharpFullMemberFormatter {
5024 protected override string GetCSharpType (string t) {
5025 string moddedType = base.GetCSharpType (t);
5027 switch (moddedType) {
5028 case "int": return "nint";
5033 case "System.Drawing.SizeF":
5034 return "CoreGraphics.CGSize";
5035 case "System.Drawing.PointF":
5036 return "CoreGraphics.CGPoint";
5037 case "System.Drawing.RectangleF":
5038 return "CoreGraphics.CGPoint";
5044 class CSharpFullMemberFormatter : MemberFormatter {
5046 public override string Language {
5050 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5053 string ns = DocUtils.GetNamespace (type);
5054 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
5055 buf.Append (ns).Append ('.');
5059 protected virtual string GetCSharpType (string t)
5062 case "System.Byte": return "byte";
5063 case "System.SByte": return "sbyte";
5064 case "System.Int16": return "short";
5065 case "System.Int32": return "int";
5066 case "System.Int64": return "long";
5068 case "System.UInt16": return "ushort";
5069 case "System.UInt32": return "uint";
5070 case "System.UInt64": return "ulong";
5072 case "System.Single": return "float";
5073 case "System.Double": return "double";
5074 case "System.Decimal": return "decimal";
5075 case "System.Boolean": return "bool";
5076 case "System.Char": return "char";
5077 case "System.Void": return "void";
5078 case "System.String": return "string";
5079 case "System.Object": return "object";
5084 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
5086 if (context != null && context.TransformFlags != null &&
5087 (context.TransformFlags.Count == 0 || context.TransformFlags [context.TransformIndex])) {
5088 context.TransformIndex++;
5089 return buf.Append ("dynamic");
5092 if (type is GenericParameter)
5093 return AppendGenericParameterConstraints (buf, (GenericParameter) type, context).Append (type.Name);
5094 string t = type.FullName;
5095 if (!t.StartsWith ("System.")) {
5096 return base.AppendTypeName (buf, type, context);
5099 string s = GetCSharpType (t);
5101 if (context != null)
5102 context.TransformIndex++;
5103 return buf.Append (s);
5106 return base.AppendTypeName (buf, type, context);
5109 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
5111 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
5113 GenericParameterAttributes attrs = type.Attributes;
5114 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
5115 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
5119 buf.Append ("out ");
5123 protected override string GetTypeDeclaration (TypeDefinition type)
5125 string visibility = GetTypeVisibility (type.Attributes);
5126 if (visibility == null)
5129 StringBuilder buf = new StringBuilder ();
5131 buf.Append (visibility);
5134 MemberFormatter full = new CSharpFullMemberFormatter ();
5136 if (DocUtils.IsDelegate (type)) {
5137 buf.Append("delegate ");
5138 MethodDefinition invoke = type.GetMethod ("Invoke");
5139 buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
5140 buf.Append (GetName (type));
5141 AppendParameters (buf, invoke, invoke.Parameters);
5142 AppendGenericTypeConstraints (buf, type);
5145 return buf.ToString();
5148 if (type.IsAbstract && !type.IsInterface)
5149 buf.Append("abstract ");
5150 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
5151 buf.Append("sealed ");
5152 buf.Replace ("abstract sealed", "static");
5154 buf.Append (GetTypeKind (type));
5156 buf.Append (GetCSharpType (type.FullName) == null
5161 TypeReference basetype = type.BaseType;
5162 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
5165 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
5166 .Select (iface => full.GetName (iface))
5170 if (basetype != null || interface_names.Count > 0)
5173 if (basetype != null) {
5174 buf.Append (full.GetName (basetype));
5175 if (interface_names.Count > 0)
5179 for (int i = 0; i < interface_names.Count; i++){
5182 buf.Append (interface_names [i]);
5184 AppendGenericTypeConstraints (buf, type);
5187 return buf.ToString ();
5190 static string GetTypeKind (TypeDefinition t)
5196 if (t.IsClass || t.FullName == "System.Enum")
5200 throw new ArgumentException(t.FullName);
5203 static string GetTypeVisibility (TypeAttributes ta)
5205 switch (ta & TypeAttributes.VisibilityMask) {
5206 case TypeAttributes.Public:
5207 case TypeAttributes.NestedPublic:
5210 case TypeAttributes.NestedFamily:
5211 case TypeAttributes.NestedFamORAssem:
5219 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
5221 if (type.GenericParameters.Count == 0)
5223 return AppendConstraints (buf, type.GenericParameters);
5226 private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
5228 foreach (GenericParameter genArg in genArgs) {
5229 GenericParameterAttributes attrs = genArg.Attributes;
5230 IList<TypeReference> constraints = genArg.Constraints;
5231 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
5234 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
5235 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
5236 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
5239 if (!isref && !isvt && !isnew && constraints.Count == 0)
5241 buf.Append (" where ").Append (genArg.Name).Append (" : ");
5243 buf.Append ("class");
5247 buf.Append ("struct");
5250 if (constraints.Count > 0 && !isvt) {
5253 buf.Append (GetTypeName (constraints [0]));
5254 for (int i = 1; i < constraints.Count; ++i)
5255 buf.Append (", ").Append (GetTypeName (constraints [i]));
5257 if (isnew && !isvt) {
5260 buf.Append ("new()");
5266 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5268 StringBuilder buf = new StringBuilder ();
5269 AppendVisibility (buf, constructor);
5270 if (buf.Length == 0)
5274 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
5275 AppendParameters (buf, constructor, constructor.Parameters);
5278 return buf.ToString ();
5281 protected override string GetMethodDeclaration (MethodDefinition method)
5283 string decl = base.GetMethodDeclaration (method);
5289 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
5291 if (DocUtils.IsExplicitlyImplemented (method)) {
5292 TypeReference iface;
5293 MethodReference ifaceMethod;
5294 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5295 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
5297 .Append (ifaceMethod.Name);
5299 return base.AppendMethodName (buf, method);
5302 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
5304 if (method.GenericParameters.Count == 0)
5306 return AppendConstraints (buf, method.GenericParameters);
5309 protected override string RefTypeModifier {
5313 protected override string GetFinalizerName (MethodDefinition method)
5315 return "~" + method.DeclaringType.Name + " ()";
5318 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
5322 if (method.IsPublic)
5323 return buf.Append ("public");
5324 if (method.IsFamily || method.IsFamilyOrAssembly)
5325 return buf.Append ("protected");
5329 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
5331 string modifiers = String.Empty;
5332 if (method.IsStatic) modifiers += " static";
5333 if (method.IsVirtual && !method.IsAbstract) {
5334 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
5335 else modifiers += " override";
5337 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
5338 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
5339 if (method.IsFinal) modifiers += " sealed";
5340 if (modifiers == " virtual sealed") modifiers = "";
5342 return buf.Append (modifiers);
5345 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
5347 if (method.IsGenericMethod ()) {
5348 IList<GenericParameter> args = method.GenericParameters;
5349 if (args.Count > 0) {
5351 buf.Append (args [0].Name);
5352 for (int i = 1; i < args.Count; ++i)
5353 buf.Append (",").Append (args [i].Name);
5360 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
5362 return AppendParameters (buf, method, parameters, '(', ')');
5365 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
5369 if (parameters.Count > 0) {
5370 if (DocUtils.IsExtensionMethod (method))
5371 buf.Append ("this ");
5372 AppendParameter (buf, parameters [0]);
5373 for (int i = 1; i < parameters.Count; ++i) {
5375 AppendParameter (buf, parameters [i]);
5379 return buf.Append (end);
5382 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
5384 if (parameter.ParameterType is ByReferenceType) {
5385 if (parameter.IsOut)
5386 buf.Append ("out ");
5388 buf.Append ("ref ");
5390 buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
5391 buf.Append (parameter.Name);
5392 if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant) {
5393 buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
5398 protected override string GetPropertyDeclaration (PropertyDefinition property)
5400 MethodDefinition method;
5402 string get_visible = null;
5403 if ((method = property.GetMethod) != null &&
5404 (DocUtils.IsExplicitlyImplemented (method) ||
5405 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5406 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5407 string set_visible = null;
5408 if ((method = property.SetMethod) != null &&
5409 (DocUtils.IsExplicitlyImplemented (method) ||
5410 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5411 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5413 if ((set_visible == null) && (get_visible == null))
5417 StringBuilder buf = new StringBuilder ();
5418 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
5419 buf.Append (visibility = get_visible);
5420 else if (set_visible != null && get_visible == null)
5421 buf.Append (visibility = set_visible);
5423 buf.Append (visibility = "public");
5425 // Pick an accessor to use for static/virtual/override/etc. checks.
5426 method = property.SetMethod;
5428 method = property.GetMethod;
5430 string modifiers = String.Empty;
5431 if (method.IsStatic) modifiers += " static";
5432 if (method.IsVirtual && !method.IsAbstract) {
5433 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
5434 modifiers += " virtual";
5436 modifiers += " override";
5438 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
5439 if (method.IsAbstract && !declDef.IsInterface)
5440 modifiers += " abstract";
5442 modifiers += " sealed";
5443 if (modifiers == " virtual sealed")
5445 buf.Append (modifiers).Append (' ');
5447 buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
5449 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
5450 string name = property.Name;
5451 foreach (MemberReference mi in defs) {
5452 if (mi == property) {
5457 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
5459 if (property.Parameters.Count != 0) {
5460 AppendParameters (buf, method, property.Parameters, '[', ']');
5464 if (get_visible != null) {
5465 if (get_visible != visibility)
5466 buf.Append (' ').Append (get_visible);
5467 buf.Append (" get;");
5469 if (set_visible != null) {
5470 if (set_visible != visibility)
5471 buf.Append (' ').Append (set_visible);
5472 buf.Append (" set;");
5476 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
5479 protected override string GetFieldDeclaration (FieldDefinition field)
5481 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
5482 if (declType.IsEnum && field.Name == "value__")
5483 return null; // This member of enums aren't documented.
5485 StringBuilder buf = new StringBuilder ();
5486 AppendFieldVisibility (buf, field);
5487 if (buf.Length == 0)
5490 if (declType.IsEnum)
5493 if (field.IsStatic && !field.IsLiteral)
5494 buf.Append (" static");
5495 if (field.IsInitOnly)
5496 buf.Append (" readonly");
5497 if (field.IsLiteral)
5498 buf.Append (" const");
5500 buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
5501 buf.Append (field.Name);
5502 AppendFieldValue (buf, field);
5505 return buf.ToString ();
5508 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
5511 return buf.Append ("public");
5512 if (field.IsFamily || field.IsFamilyOrAssembly)
5513 return buf.Append ("protected");
5517 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
5519 // enums have a value__ field, which we ignore
5520 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
5521 field.DeclaringType.IsGenericType ())
5523 if (field.HasConstant && field.IsLiteral) {
5526 val = field.Constant;
5531 buf.Append (" = ").Append ("null");
5532 else if (val is Enum)
5533 buf.Append (" = ").Append (val.ToString ());
5534 else if (val is IFormattable) {
5535 string value = ((IFormattable)val).ToString(null, CultureInfo.InvariantCulture);
5537 value = "\"" + value + "\"";
5538 buf.Append (" = ").Append (value);
5544 protected override string GetEventDeclaration (EventDefinition e)
5546 StringBuilder buf = new StringBuilder ();
5547 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
5551 AppendModifiers (buf, e.AddMethod);
5553 buf.Append (" event ");
5554 buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters [0]))).Append (' ');
5555 buf.Append (e.Name).Append (';');
5557 return buf.ToString ();
5561 class CSharpMemberFormatter : CSharpFullMemberFormatter {
5562 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5568 class DocTypeFullMemberFormatter : MemberFormatter {
5569 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
5571 protected override char NestedTypeSeparator {
5576 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
5577 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5583 class SlashDocMemberFormatter : MemberFormatter {
5585 protected override char[] GenericTypeContainer {
5586 get {return new char[]{'{', '}'};}
5589 private bool AddTypeCount = true;
5591 private TypeReference genDeclType;
5592 private MethodReference genDeclMethod;
5594 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
5596 if (type is GenericParameter) {
5598 if (genDeclType != null) {
5599 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
5600 for (int i = 0; i < genArgs.Count; ++i) {
5601 if (genArgs [i].Name == type.Name) {
5602 buf.Append ('`').Append (i);
5607 if (genDeclMethod != null) {
5608 IList<GenericParameter> genArgs = null;
5609 if (genDeclMethod.IsGenericMethod ()) {
5610 genArgs = genDeclMethod.GenericParameters;
5611 for (int i = 0; i < genArgs.Count; ++i) {
5612 if (genArgs [i].Name == type.Name) {
5613 buf.Append ("``").Append (i);
5619 if (genDeclType == null && genDeclMethod == null) {
5620 // Probably from within an explicitly implemented interface member,
5621 // where CSC uses parameter names instead of indices (why?), e.g.
5622 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
5623 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
5624 buf.Append (type.Name);
5626 if (buf.Length == l) {
5627 throw new Exception (string.Format (
5628 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
5629 type.Name, genDeclType, genDeclMethod));
5633 base.AppendTypeName (buf, type, context);
5635 int numArgs = type.GenericParameters.Count;
5636 if (type.DeclaringType != null)
5637 numArgs -= type.GenericParameters.Count;
5639 buf.Append ('`').Append (numArgs);
5646 protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
5648 buf.Append (ArrayDelimeters [0]);
5649 int rank = array.Rank;
5652 for (int i = 1; i < rank; ++i) {
5656 return buf.Append (ArrayDelimeters [1]);
5659 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5662 base.AppendGenericType (buf, type, context);
5664 AppendType (buf, type, context);
5668 private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5670 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
5671 bool insertNested = false;
5672 int prevParamCount = 0;
5673 foreach (var decl in decls) {
5675 buf.Append (NestedTypeSeparator);
5676 insertNested = true;
5677 base.AppendTypeName (buf, decl, context);
5678 int argCount = DocUtils.GetGenericArgumentCount (decl);
5679 int numArgs = argCount - prevParamCount;
5680 prevParamCount = argCount;
5682 buf.Append ('`').Append (numArgs);
5687 public override string GetDeclaration (MemberReference member)
5689 TypeReference r = member as TypeReference;
5691 return "T:" + GetTypeName (r);
5693 return base.GetDeclaration (member);
5696 protected override string GetConstructorName (MethodReference constructor)
5698 return GetMethodDefinitionName (constructor, "#ctor");
5701 protected override string GetMethodName (MethodReference method)
5704 MethodDefinition methodDef = method as MethodDefinition;
5705 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
5708 TypeReference iface;
5709 MethodReference ifaceMethod;
5710 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
5711 AddTypeCount = false;
5712 name = GetTypeName (iface) + "." + ifaceMethod.Name;
5713 AddTypeCount = true;
5715 return GetMethodDefinitionName (method, name);
5718 private string GetMethodDefinitionName (MethodReference method, string name)
5720 StringBuilder buf = new StringBuilder ();
5721 buf.Append (GetTypeName (method.DeclaringType));
5723 buf.Append (name.Replace (".", "#"));
5724 if (method.IsGenericMethod ()) {
5725 IList<GenericParameter> genArgs = method.GenericParameters;
5726 if (genArgs.Count > 0)
5727 buf.Append ("``").Append (genArgs.Count);
5729 IList<ParameterDefinition> parameters = method.Parameters;
5731 genDeclType = method.DeclaringType;
5732 genDeclMethod = method;
5733 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
5737 genDeclMethod = null;
5739 return buf.ToString ();
5742 private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
5744 if (parameters.Count == 0)
5749 AppendParameter (buf, genArgs, parameters [0]);
5750 for (int i = 1; i < parameters.Count; ++i) {
5752 AppendParameter (buf, genArgs, parameters [i]);
5755 return buf.Append (')');
5758 private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
5760 AddTypeCount = false;
5761 buf.Append (GetTypeName (parameter.ParameterType));
5762 AddTypeCount = true;
5766 protected override string GetPropertyName (PropertyReference property)
5770 PropertyDefinition propertyDef = property as PropertyDefinition;
5771 MethodDefinition method = null;
5772 if (propertyDef != null)
5773 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
5774 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
5775 name = property.Name;
5777 TypeReference iface;
5778 MethodReference ifaceMethod;
5779 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5780 AddTypeCount = false;
5781 name = string.Join ("#", new string[]{
5782 GetTypeName (iface).Replace (".", "#"),
5783 DocUtils.GetMember (property.Name)
5785 AddTypeCount = true;
5788 StringBuilder buf = new StringBuilder ();
5789 buf.Append (GetName (property.DeclaringType));
5792 IList<ParameterDefinition> parameters = property.Parameters;
5793 if (parameters.Count > 0) {
5794 genDeclType = property.DeclaringType;
5796 IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
5797 AppendParameter (buf, genArgs, parameters [0]);
5798 for (int i = 1; i < parameters.Count; ++i) {
5800 AppendParameter (buf, genArgs, parameters [i]);
5805 return buf.ToString ();
5808 protected override string GetFieldName (FieldReference field)
5810 return string.Format ("{0}.{1}",
5811 GetName (field.DeclaringType), field.Name);
5814 protected override string GetEventName (EventReference e)
5816 return string.Format ("{0}.{1}",
5817 GetName (e.DeclaringType), e.Name);
5820 protected override string GetTypeDeclaration (TypeDefinition type)
5822 string name = GetName (type);
5828 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5830 string name = GetName (constructor);
5836 protected override string GetMethodDeclaration (MethodDefinition method)
5838 string name = GetName (method);
5841 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
5842 genDeclType = method.DeclaringType;
5843 genDeclMethod = method;
5844 name += "~" + GetName (method.ReturnType);
5846 genDeclMethod = null;
5851 protected override string GetPropertyDeclaration (PropertyDefinition property)
5853 string name = GetName (property);
5859 protected override string GetFieldDeclaration (FieldDefinition field)
5861 string name = GetName (field);
5867 protected override string GetEventDeclaration (EventDefinition e)
5869 string name = GetName (e);
5876 class FileNameMemberFormatter : SlashDocMemberFormatter {
5877 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5882 protected override char NestedTypeSeparator {
5887 class ResolvedTypeInfo {
5888 TypeDefinition typeDef;
5890 public ResolvedTypeInfo (TypeReference value) {
5894 public TypeReference Reference { get; private set; }
5896 public TypeDefinition Definition {
5898 if (typeDef == null) {
5899 typeDef = Reference.Resolve ();
5906 /// <summary>Formats attribute values. Should return true if it is able to format the value.</summary>
5907 class AttributeValueFormatter {
5908 public virtual bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5910 TypeReference valueType = type.Reference;
5912 returnvalue = "null";
5915 if (valueType.FullName == "System.Type") {
5916 var vTypeRef = v as TypeReference;
5917 if (vTypeRef != null)
5918 returnvalue = "typeof(" + NativeTypeManager.GetTranslatedName (vTypeRef) + ")"; // TODO: drop NS handling
5920 returnvalue = "typeof(" + v.ToString () + ")";
5924 if (valueType.FullName == "System.String") {
5925 returnvalue = "\"" + v.ToString () + "\"";
5928 if (valueType.FullName == "System.Char") {
5929 returnvalue = "'" + v.ToString () + "'";
5933 returnvalue = (bool)v ? "true" : "false";
5937 TypeDefinition valueDef = type.Definition;
5938 if (valueDef == null || !valueDef.IsEnum) {
5939 returnvalue = v.ToString ();
5943 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5944 var values = MDocUpdater.GetEnumerationValues (valueDef);
5945 long c = MDocUpdater.ToInt64 (v);
5946 if (values.ContainsKey (c)) {
5947 returnvalue = typename + "." + values [c];
5956 /// <summary>The final value formatter in the pipeline ... if no other formatter formats the value,
5957 /// then this one will serve as the default implementation.</summary>
5958 class DefaultAttributeValueFormatter : AttributeValueFormatter {
5959 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5961 returnvalue = "(" + MDocUpdater.GetDocTypeFullName (type.Reference) + ") " + v.ToString ();
5966 /// <summary>Flags enum formatter that assumes powers of two values.</summary>
5967 /// <remarks>As described here: https://msdn.microsoft.com/en-us/library/vstudio/ms229062(v=vs.100).aspx</remarks>
5968 class StandardFlagsEnumFormatter : AttributeValueFormatter {
5969 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5971 TypeReference valueType = type.Reference;
5972 TypeDefinition valueDef = type.Definition;
5973 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
5975 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5976 var values = MDocUpdater.GetEnumerationValues (valueDef);
5977 long c = MDocUpdater.ToInt64 (v);
5978 returnvalue = string.Join (" | ",
5979 (from i in values.Keys
5980 where (c & i) == i && i != 0
5981 select typename + "." + values [i])
5982 .DefaultIfEmpty (c.ToString ()).ToArray ());
5992 /// <summary>A custom formatter for the ObjCRuntime.Platform enumeration.</summary>
5993 class ApplePlatformEnumFormatter : AttributeValueFormatter {
5994 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5996 TypeReference valueType = type.Reference;
5997 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5998 TypeDefinition valueDef = type.Definition;
5999 if (typename.Contains ("ObjCRuntime.Platform") && valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
6001 var values = MDocUpdater.GetEnumerationValues (valueDef);
6002 long c = MDocUpdater.ToInt64 (v);
6004 returnvalue = Format (c, values, typename);
6012 string Format (long c, IDictionary<long, string> values, string typename)
6014 int iosarch, iosmajor, iosminor, iossubminor;
6015 int macarch, macmajor, macminor, macsubminor;
6016 GetEncodingiOS (c, out iosarch, out iosmajor, out iosminor, out iossubminor);
6017 GetEncodingMac ((ulong)c, out macarch, out macmajor, out macminor, out macsubminor);
6019 if (iosmajor == 0 & iosminor == 0 && iossubminor == 0) {
6020 return FormatValues ("Mac", macarch, macmajor, macminor, macsubminor);
6023 if (macmajor == 0 & macminor == 0 && macsubminor == 0) {
6024 return FormatValues ("iOS", iosarch, iosmajor, iosminor, iossubminor);
6027 return string.Format ("(Platform){0}", c);
6030 string FormatValues (string plat, int arch, int major, int minor, int subminor)
6032 string archstring = "";
6041 return string.Format ("Platform.{4}_{0}_{1}{2} | Platform.{4}_Arch{3}",
6044 subminor == 0 ? "" : "_" + subminor.ToString (),
6050 void GetEncodingiOS (long entireLong, out int archindex, out int major, out int minor, out int subminor)
6052 long lowerBits = entireLong & 0xffffffff;
6053 int lowerBitsAsInt = (int) lowerBits;
6054 GetEncoding (lowerBitsAsInt, out archindex, out major, out minor, out subminor);
6057 void GetEncodingMac (ulong entireLong, out int archindex, out int major, out int minor, out int subminor)
6059 ulong higherBits = entireLong & 0xffffffff00000000;
6060 int higherBitsAsInt = (int) ((higherBits) >> 32);
6061 GetEncoding (higherBitsAsInt, out archindex, out major, out minor, out subminor);
6064 void GetEncoding (Int32 encodedBits, out int archindex, out int major, out int minor, out int subminor)
6066 // format is AAJJNNSS
6067 archindex = (int)((encodedBits & 0xFF000000) >> 24);
6068 major = (int)((encodedBits & 0x00FF0000) >> 16);
6069 minor = (int)((encodedBits & 0x0000FF00) >> 8);
6070 subminor = (int)((encodedBits & 0x000000FF) >> 0);