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 TypeReference GetUnderlyingType (this TypeDefinition type)
3014 return type.Fields.First (f => f.Name == "value__").FieldType;
3017 public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
3019 return self.Types.SelectMany (t => t.GetAllTypes ());
3022 static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
3026 if (!self.HasNestedTypes)
3029 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
3039 static class DocUtils {
3041 public static bool DoesNotHaveApiStyle(this XmlElement element, ApiStyle style) {
3042 string styleString = style.ToString ().ToLowerInvariant ();
3043 string apistylevalue = element.GetAttribute ("apistyle");
3044 return apistylevalue != styleString || string.IsNullOrWhiteSpace(apistylevalue);
3046 public static bool HasApiStyle(this XmlElement element, ApiStyle style) {
3047 string styleString = style.ToString ().ToLowerInvariant ();
3048 return element.GetAttribute ("apistyle") == styleString;
3050 public static bool HasApiStyle(this XmlNode node, ApiStyle style)
3052 var attribute = node.Attributes ["apistyle"];
3053 return attribute != null && attribute.Value == style.ToString ().ToLowerInvariant ();
3055 public static void AddApiStyle(this XmlElement element, ApiStyle style) {
3056 string styleString = style.ToString ().ToLowerInvariant ();
3057 var existingValue = element.GetAttribute ("apistyle");
3058 if (string.IsNullOrWhiteSpace (existingValue) || existingValue != styleString) {
3059 element.SetAttribute ("apistyle", styleString);
3062 // Propagate the API style up to the membernode if necessary
3063 if (element.LocalName == "AssemblyInfo" && element.ParentNode != null && element.ParentNode.LocalName == "Member") {
3064 var member = element.ParentNode;
3065 var unifiedAssemblyNode = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']");
3066 var classicAssemblyNode = member.SelectSingleNode ("AssemblyInfo[not(@apistyle) or @apistyle='classic']");
3068 var parentAttribute = element.ParentNode.Attributes ["apistyle"];
3069 Action removeStyle = () => element.ParentNode.Attributes.Remove (parentAttribute);
3070 Action propagateStyle = () => {
3071 if (parentAttribute == null) {
3072 // if it doesn't have the attribute, then add it
3073 parentAttribute = element.OwnerDocument.CreateAttribute ("apistyle");
3074 parentAttribute.Value = styleString;
3075 element.ParentNode.Attributes.Append (parentAttribute);
3079 if ((style == ApiStyle.Classic && unifiedAssemblyNode != null) || (style == ApiStyle.Unified && classicAssemblyNode != null))
3085 public static void AddApiStyle (this XmlNode node, ApiStyle style)
3087 string styleString = style.ToString ().ToLowerInvariant ();
3088 var existingAttribute = node.Attributes ["apistyle"];
3089 if (existingAttribute == null) {
3090 existingAttribute = node.OwnerDocument.CreateAttribute ("apistyle");
3091 node.Attributes.Append (existingAttribute);
3093 existingAttribute.Value = styleString;
3095 public static void RemoveApiStyle (this XmlElement element, ApiStyle style)
3097 string styleString = style.ToString ().ToLowerInvariant ();
3098 string existingValue = element.GetAttribute ("apistyle");
3099 if (string.IsNullOrWhiteSpace (existingValue) || existingValue == styleString) {
3100 element.RemoveAttribute ("apistyle");
3103 public static void RemoveApiStyle (this XmlNode node, ApiStyle style)
3105 var styleAttribute = node.Attributes ["apistyle"];
3106 if (styleAttribute != null && styleAttribute.Value == style.ToString ().ToLowerInvariant ()) {
3107 node.Attributes.Remove (styleAttribute);
3111 public static bool IsExplicitlyImplemented (MethodDefinition method)
3113 return method.IsPrivate && method.IsFinal && method.IsVirtual;
3116 public static string GetTypeDotMember (string name)
3118 int startType, startMethod;
3119 startType = startMethod = -1;
3120 for (int i = 0; i < name.Length; ++i) {
3121 if (name [i] == '.') {
3122 startType = startMethod;
3126 return name.Substring (startType+1);
3129 public static string GetMember (string name)
3131 int i = name.LastIndexOf ('.');
3134 return name.Substring (i+1);
3137 public static void GetInfoForExplicitlyImplementedMethod (
3138 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
3142 if (method.Overrides.Count != 1)
3143 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
3144 iface = method.Overrides [0].DeclaringType;
3145 ifaceMethod = method.Overrides [0];
3148 public static string GetPropertyName (PropertyDefinition pi)
3150 // Issue: (g)mcs-generated assemblies that explicitly implement
3151 // properties don't specify the full namespace, just the
3152 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
3153 MethodDefinition method = pi.GetMethod;
3155 method = pi.SetMethod;
3156 if (!IsExplicitlyImplemented (method))
3159 // Need to determine appropriate namespace for this member.
3160 TypeReference iface;
3161 MethodReference ifaceMethod;
3162 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3163 return string.Join (".", new string[]{
3164 DocTypeFullMemberFormatter.Default.GetName (iface),
3165 GetMember (pi.Name)});
3168 public static string GetNamespace (TypeReference type)
3170 if (type.GetElementType ().IsNested)
3171 type = type.GetElementType ();
3172 while (type != null && type.IsNested)
3173 type = type.DeclaringType;
3175 return string.Empty;
3177 string typeNS = type.Namespace;
3179 // first, make sure this isn't a type reference to another assembly/module
3181 bool isInAssembly = MDocUpdater.IsInAssemblies(type.Module.Name);
3182 if (isInAssembly && !typeNS.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (type)) {
3183 typeNS = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typeNS);
3188 public static string PathCombine (string dir, string path)
3194 return Path.Combine (dir, path);
3197 public static bool IsExtensionMethod (MethodDefinition method)
3200 method.CustomAttributes
3201 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
3202 && method.DeclaringType.CustomAttributes
3203 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
3206 public static bool IsDelegate (TypeDefinition type)
3208 TypeReference baseRef = type.BaseType;
3209 if (baseRef == null)
3211 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
3212 baseRef.FullName == "System.MulticastDelegate";
3215 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
3217 List<TypeReference> decls = new List<TypeReference> ();
3219 while (type.DeclaringType != null) {
3220 decls.Add (type.DeclaringType);
3221 type = type.DeclaringType;
3227 public static int GetGenericArgumentCount (TypeReference type)
3229 GenericInstanceType inst = type as GenericInstanceType;
3231 ? inst.GenericArguments.Count
3232 : type.GenericParameters.Count;
3235 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
3237 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
3238 List<TypeReference> userInterfaces = new List<TypeReference> ();
3239 foreach (var ii in type.Interfaces) {
3240 var iface = ii.InterfaceType;
3241 TypeReference lookup = iface.Resolve () ?? iface;
3242 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
3243 userInterfaces.Add (iface);
3245 return userInterfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()));
3248 private static string GetQualifiedTypeName (TypeReference type)
3250 return "[" + type.Scope.Name + "]" + type.FullName;
3253 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
3255 HashSet<string> inheritedInterfaces = new HashSet<string> ();
3256 Action<TypeDefinition> a = null;
3258 if (t == null) return;
3259 foreach (var r in t.Interfaces) {
3260 inheritedInterfaces.Add (GetQualifiedTypeName (r.InterfaceType));
3261 a (r.InterfaceType.Resolve ());
3264 TypeReference baseRef = type.BaseType;
3265 while (baseRef != null) {
3266 TypeDefinition baseDef = baseRef.Resolve ();
3267 if (baseDef != null) {
3269 baseRef = baseDef.BaseType;
3274 foreach (var r in type.Interfaces)
3275 a (r.InterfaceType.Resolve ());
3276 return inheritedInterfaces;
3280 class DocsNodeInfo {
3281 public DocsNodeInfo (XmlElement node)
3286 public DocsNodeInfo (XmlElement node, TypeDefinition type)
3292 public DocsNodeInfo (XmlElement node, MemberReference member)
3295 SetMemberInfo (member);
3298 void SetType (TypeDefinition type)
3301 throw new ArgumentNullException ("type");
3303 GenericParameters = new List<GenericParameter> (type.GenericParameters);
3304 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
3305 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
3306 for (int i = 0; i < declTypes.Count - 1; ++i) {
3307 int remove = System.Math.Min (maxGenArgs,
3308 DocUtils.GetGenericArgumentCount (declTypes [i]));
3309 maxGenArgs -= remove;
3310 while (remove-- > 0)
3311 GenericParameters.RemoveAt (0);
3313 if (DocUtils.IsDelegate (type)) {
3314 Parameters = type.GetMethod("Invoke").Parameters;
3315 ReturnType = type.GetMethod("Invoke").ReturnType;
3316 ReturnIsReturn = true;
3320 void SetMemberInfo (MemberReference member)
3323 throw new ArgumentNullException ("member");
3324 ReturnIsReturn = true;
3328 if (member is MethodReference ) {
3329 MethodReference mr = (MethodReference) member;
3330 Parameters = mr.Parameters;
3331 if (mr.IsGenericMethod ()) {
3332 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
3335 else if (member is PropertyDefinition) {
3336 Parameters = ((PropertyDefinition) member).Parameters;
3339 if (member is MethodDefinition) {
3340 ReturnType = ((MethodDefinition) member).ReturnType;
3341 } else if (member is PropertyDefinition) {
3342 ReturnType = ((PropertyDefinition) member).PropertyType;
3343 ReturnIsReturn = false;
3346 // no remarks section for enum members
3347 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
3351 public TypeReference ReturnType;
3352 public List<GenericParameter> GenericParameters;
3353 public IList<ParameterDefinition> Parameters;
3354 public bool ReturnIsReturn;
3355 public XmlElement Node;
3356 public bool AddRemarks = true;
3357 public MemberReference Member;
3358 public TypeDefinition Type;
3360 public override string ToString ()
3362 return string.Format ("{0} - {1} - {2}", Type, Member, Node == null ? "no xml" : "with xml");
3366 class DocumentationEnumerator {
3368 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3370 return GetDocumentationTypes (assembly, forTypes, null);
3373 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3375 foreach (TypeDefinition type in assembly.GetTypes()) {
3376 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
3378 if (seen != null && seen.Contains (type.FullName))
3381 foreach (TypeDefinition nested in type.NestedTypes)
3382 yield return nested;
3386 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3388 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
3389 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
3390 oldmember.RemoveAttribute ("__monodocer-seen__");
3393 MemberReference m = GetMember (type, new DocumentationMember (oldmember));
3395 yield return new DocsNodeInfo (oldmember);
3398 yield return new DocsNodeInfo (oldmember, m);
3403 protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
3405 string membertype = member.MemberType;
3407 string returntype = member.ReturnType;
3409 string docName = member.MemberName;
3411 string[] docTypeParams = GetTypeParameters (docName, member.TypeParameters);
3413 // If we're using 'magic types', then we might get false positives ... in those cases, we keep searching
3414 MemberReference likelyCandidate = null;
3416 // Loop through all members in this type with the same name
3417 var reflectedMembers = GetReflectionMembers (type, docName).ToArray ();
3418 foreach (MemberReference mi in reflectedMembers) {
3419 bool matchedMagicType = false;
3420 if (mi is TypeDefinition) continue;
3421 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
3423 if (MDocUpdater.IsPrivate (mi))
3426 IList<ParameterDefinition> pis = null;
3427 string[] typeParams = null;
3428 if (mi is MethodDefinition) {
3429 MethodDefinition mb = (MethodDefinition) mi;
3430 pis = mb.Parameters;
3431 if (mb.IsGenericMethod ()) {
3432 IList<GenericParameter> args = mb.GenericParameters;
3433 typeParams = args.Select (p => p.Name).ToArray ();
3436 else if (mi is PropertyDefinition)
3437 pis = ((PropertyDefinition)mi).Parameters;
3439 // check type parameters
3440 int methodTcount = member.TypeParameters == null ? 0 : member.TypeParameters.Count;
3441 int reflectionTcount = typeParams == null ? 0 : typeParams.Length;
3442 if (methodTcount != reflectionTcount)
3445 // check member parameters
3446 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
3447 int pcount = pis == null ? 0 : pis.Count;
3448 if (mcount != pcount)
3451 MethodDefinition mDef = mi as MethodDefinition;
3452 if (mDef != null && !mDef.IsConstructor) {
3453 // Casting operators can overload based on return type.
3454 string rtype = GetReplacedString (
3455 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
3456 typeParams, docTypeParams);
3457 string originalRType = rtype;
3458 if (MDocUpdater.SwitchingToMagicTypes) {
3459 rtype = NativeTypeManager.ConvertFromNativeType (rtype);
3462 if ((returntype != rtype && originalRType == rtype) ||
3463 (MDocUpdater.SwitchingToMagicTypes && returntype != originalRType && returntype != rtype && originalRType != rtype)) {
3467 if (originalRType != rtype)
3468 matchedMagicType = true;
3474 for (int i = 0; i < pis.Count; i++) {
3475 string paramType = GetReplacedString (
3476 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
3477 typeParams, docTypeParams);
3479 // if magictypes, replace paramType to "classic value" ... so the comparison works
3480 string originalParamType = paramType;
3481 if (MDocUpdater.SwitchingToMagicTypes) {
3482 paramType = NativeTypeManager.ConvertFromNativeType (paramType);
3485 string xmlMemberType = member.Parameters [i];
3486 if ((!paramType.Equals(xmlMemberType) && paramType.Equals(originalParamType)) ||
3487 (MDocUpdater.SwitchingToMagicTypes && !originalParamType.Equals(xmlMemberType) && !paramType.Equals(xmlMemberType) && !paramType.Equals(originalParamType))) {
3489 // did not match ... if we're dropping the namespace, and the paramType has the dropped
3490 // namespace, we should see if it matches when added
3491 bool stillDoesntMatch = true;
3492 if (MDocUpdater.HasDroppedNamespace(type) && paramType.StartsWith (MDocUpdater.droppedNamespace)) {
3493 string withDroppedNs = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, xmlMemberType);
3495 stillDoesntMatch = withDroppedNs != paramType;
3498 if (stillDoesntMatch) {
3504 if (originalParamType != paramType)
3505 matchedMagicType = true;
3507 if (!good) continue;
3509 if (MDocUpdater.SwitchingToMagicTypes && likelyCandidate == null && matchedMagicType) {
3510 // 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
3511 likelyCandidate = mi;
3518 return likelyCandidate;
3521 static string[] GetTypeParameters (string docName, IEnumerable<string> knownParameters)
3523 if (docName [docName.Length-1] != '>')
3525 StringList types = new StringList ();
3526 int endToken = docName.Length-2;
3527 int i = docName.Length-2;
3529 if (docName [i] == ',' || docName [i] == '<') {
3530 types.Add (docName.Substring (i + 1, endToken - i));
3533 if (docName [i] == '<')
3538 var arrayTypes = types.ToArray ();
3540 if (knownParameters != null && knownParameters.Any () && arrayTypes.Length != knownParameters.Count ())
3541 return knownParameters.ToArray ();
3546 protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
3548 // In case of dropping the namespace, we have to remove the dropped NS
3549 // so that docName will match what's in the assembly/type
3550 if (MDocUpdater.HasDroppedNamespace (type) && docName.StartsWith(MDocUpdater.droppedNamespace + ".")) {
3551 int droppedNsLength = MDocUpdater.droppedNamespace.Length;
3552 docName = docName.Substring (droppedNsLength + 1, docName.Length - droppedNsLength - 1);
3555 // need to worry about 4 forms of //@MemberName values:
3556 // 1. "Normal" (non-generic) member names: GetEnumerator
3558 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
3559 // - try as-is, and try type.member (due to "kludge" for property
3561 // 3. "Normal" Generic member names: Sort<T> (CSC)
3562 // - need to remove generic parameters --> "Sort"
3563 // 4. Explicitly-implemented interface members for generic interfaces:
3564 // -- System.Collections.Generic.IEnumerable<T>.Current
3565 // - Try as-is, and try type.member, *keeping* the generic parameters.
3566 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
3567 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
3568 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
3569 // this as (1) or (2).
3570 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
3572 foreach (MemberReference mi in type.GetMembers (docName))
3574 if (CountChars (docName, '.') > 0)
3575 // might be a property; try only type.member instead of
3576 // namespace.type.member.
3577 foreach (MemberReference mi in
3578 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
3585 int startLt, startType, startMethod;
3586 startLt = startType = startMethod = -1;
3587 for (int i = 0; i < docName.Length; ++i) {
3588 switch (docName [i]) {
3597 if (numLt == 0 && (i + 1) < docName.Length)
3598 // there's another character in docName, so this <...> sequence is
3599 // probably part of a generic type -- case 4.
3603 startType = startMethod;
3609 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
3611 foreach (MemberReference mi in type.GetMembers (refName))
3615 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
3618 // If we _still_ haven't found it, we've hit another generic naming issue:
3619 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
3620 // explicitly-implemented METHOD names (not properties), e.g.
3621 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
3622 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
3623 // which the XML docs will contain.
3625 // Alas, we can't derive the Mono name from docName, so we need to iterate
3626 // over all member names, convert them into CSC format, and compare... :-(
3629 foreach (MemberReference mi in type.GetMembers ()) {
3630 if (MDocUpdater.GetMemberName (mi) == docName)
3635 static string GetReplacedString (string typeName, string[] from, string[] to)
3639 for (int i = 0; i < from.Length; ++i)
3640 typeName = typeName.Replace (from [i], to [i]);
3644 private static int CountChars (string s, char c)
3647 for (int i = 0; i < s.Length; ++i) {
3655 class EcmaDocumentationEnumerator : DocumentationEnumerator {
3660 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
3663 this.ecmadocs = ecmaDocs;
3666 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3668 HashSet<string> seen = new HashSet<string> ();
3669 return GetDocumentationTypes (assembly, forTypes, seen)
3670 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
3673 new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3676 while (ecmadocs.Read ()) {
3677 switch (ecmadocs.Name) {
3679 if (typeDepth == -1)
3680 typeDepth = ecmadocs.Depth;
3681 if (ecmadocs.NodeType != XmlNodeType.Element)
3683 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
3685 string typename = ecmadocs.GetAttribute ("FullName");
3686 string typename2 = MDocUpdater.GetTypeFileName (typename);
3687 if (forTypes != null &&
3688 forTypes.BinarySearch (typename) < 0 &&
3689 typename != typename2 &&
3690 forTypes.BinarySearch (typename2) < 0)
3693 if ((t = assembly.GetType (typename)) == null &&
3694 (t = assembly.GetType (typename2)) == null)
3696 seen.Add (typename);
3697 if (typename != typename2)
3698 seen.Add (typename2);
3699 Console.WriteLine (" Import: {0}", t.FullName);
3700 if (ecmadocs.Name != "Docs") {
3701 int depth = ecmadocs.Depth;
3702 while (ecmadocs.Read ()) {
3703 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
3707 if (!ecmadocs.IsStartElement ("Docs"))
3708 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
3718 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3720 return GetMembers (basefile, type)
3721 .Concat (base.GetDocumentationMembers (basefile, type));
3724 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
3726 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
3729 if (ecmadocs.IsEmptyElement)
3732 int membersDepth = ecmadocs.Depth;
3734 while (go && ecmadocs.Read ()) {
3735 switch (ecmadocs.Name) {
3737 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
3739 DocumentationMember dm = new DocumentationMember (ecmadocs);
3741 string xp = MDocUpdater.GetXPathForMember (dm);
3742 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
3744 if (oldmember == null) {
3745 m = GetMember (type, dm);
3747 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3748 type.FullName, dm.MemberSignatures ["C#"]);
3749 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
3752 // oldmember lookup may have failed due to type parameter renames.
3754 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
3755 if (oldmember == null) {
3756 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
3757 oldmember = basefile.CreateElement ("Member");
3758 oldmember.SetAttribute ("MemberName", dm.MemberName);
3759 members.AppendChild (oldmember);
3760 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
3761 XmlElement ms = basefile.CreateElement ("MemberSignature");
3762 ms.SetAttribute ("Language", key);
3763 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
3764 oldmember.AppendChild (ms);
3766 oldmember.SetAttribute ("__monodocer-seen__", "true");
3767 Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
3772 m = GetMember (type, new DocumentationMember (oldmember));
3774 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3775 type.FullName, dm.MemberSignatures ["C#"]);
3778 oldmember.SetAttribute ("__monodocer-seen__", "true");
3780 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
3781 if (ecmadocs.Name != "Docs")
3782 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
3787 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
3796 abstract class DocumentationImporter {
3798 public abstract void ImportDocumentation (DocsNodeInfo info);
3801 class MsxdocDocumentationImporter : DocumentationImporter {
3803 XmlDocument slashdocs;
3805 public MsxdocDocumentationImporter (string file)
3807 var xml = File.ReadAllText (file);
3809 // Ensure Unix line endings
3810 xml = xml.Replace ("\r", "");
3812 slashdocs = new XmlDocument();
3813 slashdocs.LoadXml (xml);
3816 public override void ImportDocumentation (DocsNodeInfo info)
3818 XmlNode elem = GetDocs (info.Member ?? info.Type);
3823 XmlElement e = info.Node;
3825 if (elem.SelectSingleNode("summary") != null)
3826 MDocUpdater.ClearElement(e, "summary");
3827 if (elem.SelectSingleNode("remarks") != null)
3828 MDocUpdater.ClearElement(e, "remarks");
3829 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3830 MDocUpdater.ClearElement(e, "value");
3831 MDocUpdater.ClearElement(e, "returns");
3834 foreach (XmlNode child in elem.ChildNodes) {
3835 switch (child.Name) {
3838 XmlAttribute name = child.Attributes ["name"];
3841 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3843 p2.InnerXml = child.InnerXml;
3846 // Occasionally XML documentation will use <returns/> on
3847 // properties, so let's try to normalize things.
3850 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3851 v.InnerXml = child.InnerXml;
3857 case "permission": {
3858 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3861 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3863 a = e.OwnerDocument.CreateElement (child.Name);
3864 a.SetAttribute ("cref", cref.Value);
3867 a.InnerXml = child.InnerXml;
3871 XmlAttribute cref = child.Attributes ["cref"];
3874 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3876 a = e.OwnerDocument.CreateElement ("altmember");
3877 a.SetAttribute ("cref", cref.Value);
3884 if (child.NodeType == XmlNodeType.Element &&
3885 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3888 MDocUpdater.CopyNode (child, e);
3895 private XmlNode GetDocs (MemberReference member)
3897 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3898 if (slashdocsig != null)
3899 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3904 class EcmaDocumentationImporter : DocumentationImporter {
3908 public EcmaDocumentationImporter (XmlReader ecmaDocs)
3910 this.ecmadocs = ecmaDocs;
3913 public override void ImportDocumentation (DocsNodeInfo info)
3915 if (!ecmadocs.IsStartElement ("Docs")) {
3919 XmlElement e = info.Node;
3921 int depth = ecmadocs.Depth;
3922 ecmadocs.ReadStartElement ("Docs");
3923 while (ecmadocs.Read ()) {
3924 if (ecmadocs.Name == "Docs") {
3925 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3928 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3930 if (!ecmadocs.IsStartElement ())
3932 switch (ecmadocs.Name) {
3935 string name = ecmadocs.GetAttribute ("name");
3938 XmlNode doc = e.SelectSingleNode (
3939 ecmadocs.Name + "[@name='" + name + "']");
3940 string value = ecmadocs.ReadInnerXml ();
3942 doc.InnerXml = value.Replace ("\r", "");
3949 string name = ecmadocs.Name;
3950 string cref = ecmadocs.GetAttribute ("cref");
3953 XmlNode doc = e.SelectSingleNode (
3954 ecmadocs.Name + "[@cref='" + cref + "']");
3955 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3957 doc.InnerXml = value;
3959 XmlElement n = e.OwnerDocument.CreateElement (name);
3960 n.SetAttribute ("cref", cref);
3967 string name = ecmadocs.Name;
3968 string xpath = ecmadocs.Name;
3969 StringList attributes = new StringList (ecmadocs.AttributeCount);
3970 if (ecmadocs.MoveToFirstAttribute ()) {
3972 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3973 } while (ecmadocs.MoveToNextAttribute ());
3974 ecmadocs.MoveToContent ();
3976 if (attributes.Count > 0) {
3977 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3979 XmlNode doc = e.SelectSingleNode (xpath);
3980 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3982 doc.InnerXml = value;
3985 XmlElement n = e.OwnerDocument.CreateElement (name);
3987 foreach (string a in attributes) {
3988 int eq = a.IndexOf ('=');
3989 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
4000 class DocumentationMember {
4001 public StringToStringMap MemberSignatures = new StringToStringMap ();
4002 public string ReturnType;
4003 public StringList Parameters;
4004 public StringList TypeParameters;
4005 public string MemberName;
4006 public string MemberType;
4008 public DocumentationMember (XmlReader reader)
4010 MemberName = reader.GetAttribute ("MemberName");
4011 int depth = reader.Depth;
4013 StringList p = new StringList ();
4014 StringList tp = new StringList ();
4016 if (reader.NodeType != XmlNodeType.Element)
4019 bool shouldUse = true;
4021 string apistyle = reader.GetAttribute ("apistyle");
4022 shouldUse = string.IsNullOrWhiteSpace(apistyle) || apistyle == "classic"; // only use this tag if it's an 'classic' style node
4024 catch (Exception ex) {}
4025 switch (reader.Name) {
4026 case "MemberSignature":
4028 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
4032 MemberType = reader.ReadElementString ();
4035 if (reader.Depth == depth + 2 && shouldUse)
4036 ReturnType = reader.ReadElementString ();
4039 if (reader.Depth == depth + 2 && shouldUse)
4040 p.Add (reader.GetAttribute ("Type"));
4042 case "TypeParameter":
4043 if (reader.Depth == depth + 2 && shouldUse)
4044 tp.Add (reader.GetAttribute ("Name"));
4047 if (reader.Depth == depth + 1)
4051 } while (go && reader.Read () && reader.Depth >= depth);
4056 TypeParameters = tp;
4058 DiscernTypeParameters ();
4062 public DocumentationMember (XmlNode node)
4064 MemberName = node.Attributes ["MemberName"].Value;
4065 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
4066 XmlAttribute l = n.Attributes ["Language"];
4067 XmlAttribute v = n.Attributes ["Value"];
4068 XmlAttribute apistyle = n.Attributes ["apistyle"];
4069 bool shouldUse = apistyle == null || apistyle.Value == "classic";
4070 if (l != null && v != null && shouldUse)
4071 MemberSignatures [l.Value] = v.Value;
4073 MemberType = node.SelectSingleNode ("MemberType").InnerText;
4074 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType[not(@apistyle) or @apistyle='classic']");
4076 ReturnType = rt.InnerText;
4077 XmlNodeList p = node.SelectNodes ("Parameters/Parameter[not(@apistyle) or @apistyle='classic']");
4079 Parameters = new StringList (p.Count);
4080 for (int i = 0; i < p.Count; ++i)
4081 Parameters.Add (p [i].Attributes ["Type"].Value);
4083 XmlNodeList tp = node.SelectNodes ("TypeParameters/TypeParameter[not(@apistyle) or @apistyle='classic']");
4085 TypeParameters = new StringList (tp.Count);
4086 for (int i = 0; i < tp.Count; ++i)
4087 TypeParameters.Add (tp [i].Attributes ["Name"].Value);
4090 DiscernTypeParameters ();
4094 void DiscernTypeParameters ()
4096 // see if we can discern the param list from the name
4097 if (MemberName.Contains ("<") && MemberName.EndsWith (">")) {
4098 var starti = MemberName.IndexOf ("<") + 1;
4099 var endi = MemberName.LastIndexOf (">");
4100 var paramlist = MemberName.Substring (starti, endi - starti);
4101 var tparams = paramlist.Split (new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
4102 TypeParameters = new StringList (tparams);
4107 public class DynamicParserContext {
4108 public ReadOnlyCollection<bool> TransformFlags;
4109 public int TransformIndex;
4111 public DynamicParserContext (ICustomAttributeProvider provider)
4114 if (provider.HasCustomAttributes &&
4115 (da = (provider.CustomAttributes.Cast<CustomAttribute>()
4116 .SingleOrDefault (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))) != null) {
4117 CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
4118 ? new CustomAttributeArgument [0]
4119 : (CustomAttributeArgument[]) da.ConstructorArguments [0].Value;
4121 TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool) t.Value).ToArray());
4126 public enum MemberFormatterState {
4128 WithinGenericTypeParameters,
4131 public abstract class MemberFormatter {
4133 public virtual string Language {
4137 public string GetName (MemberReference member)
4139 return GetName (member, null);
4142 public virtual string GetName (MemberReference member, DynamicParserContext context)
4144 TypeReference type = member as TypeReference;
4146 return GetTypeName (type, context);
4147 MethodReference method = member as MethodReference;
4148 if (method != null && method.Name == ".ctor") // method.IsConstructor
4149 return GetConstructorName (method);
4151 return GetMethodName (method);
4152 PropertyReference prop = member as PropertyReference;
4154 return GetPropertyName (prop);
4155 FieldReference field = member as FieldReference;
4157 return GetFieldName (field);
4158 EventReference e = member as EventReference;
4160 return GetEventName (e);
4161 throw new NotSupportedException ("Can't handle: " +
4162 (member == null ? "null" : member.GetType().ToString()));
4165 protected virtual string GetTypeName (TypeReference type)
4167 return GetTypeName (type, null);
4170 protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
4173 throw new ArgumentNullException ("type");
4174 return _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
4177 protected virtual char[] ArrayDelimeters {
4178 get {return new char[]{'[', ']'};}
4181 protected virtual MemberFormatterState MemberFormatterState { get; set; }
4183 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4185 if (type is ArrayType) {
4186 TypeSpecification spec = type as TypeSpecification;
4187 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
4188 return AppendArrayModifiers (buf, (ArrayType) type);
4190 if (type is ByReferenceType) {
4191 return AppendRefTypeName (buf, type, context);
4193 if (type is PointerType) {
4194 return AppendPointerTypeName (buf, type, context);
4196 if (type is GenericParameter) {
4197 return AppendTypeName (buf, type, context);
4199 AppendNamespace (buf, type);
4200 GenericInstanceType genInst = type as GenericInstanceType;
4201 if (type.GenericParameters.Count == 0 &&
4202 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
4203 return AppendFullTypeName (buf, type, context);
4205 return AppendGenericType (buf, type, context);
4208 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4210 string ns = DocUtils.GetNamespace (type);
4211 if (ns != null && ns.Length > 0)
4212 buf.Append (ns).Append ('.');
4216 protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4218 if (type.DeclaringType != null)
4219 AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
4220 return AppendTypeName (buf, type, context);
4223 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4225 if (context != null)
4226 context.TransformIndex++;
4227 return AppendTypeName (buf, type.Name);
4230 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
4232 int n = typename.IndexOf ("`");
4234 return buf.Append (typename.Substring (0, n));
4235 return buf.Append (typename);
4238 protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
4240 buf.Append (ArrayDelimeters [0]);
4241 int rank = array.Rank;
4243 buf.Append (new string (',', rank-1));
4244 return buf.Append (ArrayDelimeters [1]);
4247 protected virtual string RefTypeModifier {
4251 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4253 TypeSpecification spec = type as TypeSpecification;
4254 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4255 .Append (RefTypeModifier);
4258 protected virtual string PointerModifier {
4262 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4264 TypeSpecification spec = type as TypeSpecification;
4265 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4266 .Append (PointerModifier);
4269 protected virtual char[] GenericTypeContainer {
4270 get {return new char[]{'<', '>'};}
4273 protected virtual char NestedTypeSeparator {
4277 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4279 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4280 type is GenericInstanceType ? type.GetElementType () : type);
4281 List<TypeReference> genArgs = GetGenericArguments (type);
4284 bool insertNested = false;
4285 foreach (var decl in decls) {
4286 TypeReference declDef = decl.Resolve () ?? decl;
4288 buf.Append (NestedTypeSeparator);
4290 insertNested = true;
4291 AppendTypeName (buf, declDef, context);
4292 int ac = DocUtils.GetGenericArgumentCount (declDef);
4296 buf.Append (GenericTypeContainer [0]);
4297 var origState = MemberFormatterState;
4298 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4299 _AppendTypeName (buf, genArgs [argIdx++], context);
4300 for (int i = 1; i < c; ++i) {
4301 _AppendTypeName (buf.Append (","), genArgs [argIdx++], context);
4303 MemberFormatterState = origState;
4304 buf.Append (GenericTypeContainer [1]);
4310 protected List<TypeReference> GetGenericArguments (TypeReference type)
4312 var args = new List<TypeReference> ();
4313 GenericInstanceType inst = type as GenericInstanceType;
4315 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
4317 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
4321 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4326 protected virtual string GetConstructorName (MethodReference constructor)
4328 return constructor.Name;
4331 protected virtual string GetMethodName (MethodReference method)
4336 protected virtual string GetPropertyName (PropertyReference property)
4338 return property.Name;
4341 protected virtual string GetFieldName (FieldReference field)
4346 protected virtual string GetEventName (EventReference e)
4351 public string GetDeclaration (MemberReference mreference)
4353 return GetDeclaration (mreference.Resolve ());
4356 string GetDeclaration (IMemberDefinition member)
4359 throw new ArgumentNullException ("member");
4360 TypeDefinition type = member as TypeDefinition;
4362 return GetTypeDeclaration (type);
4363 MethodDefinition method = member as MethodDefinition;
4364 if (method != null && method.IsConstructor)
4365 return GetConstructorDeclaration (method);
4367 return GetMethodDeclaration (method);
4368 PropertyDefinition prop = member as PropertyDefinition;
4370 return GetPropertyDeclaration (prop);
4371 FieldDefinition field = member as FieldDefinition;
4373 return GetFieldDeclaration (field);
4374 EventDefinition e = member as EventDefinition;
4376 return GetEventDeclaration (e);
4377 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
4380 protected virtual string GetTypeDeclaration (TypeDefinition type)
4383 throw new ArgumentNullException ("type");
4384 StringBuilder buf = new StringBuilder (type.Name.Length);
4385 _AppendTypeName (buf, type, null);
4386 AppendGenericTypeConstraints (buf, type);
4387 return buf.ToString ();
4390 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
4392 return GetConstructorName (constructor);
4395 protected virtual string GetMethodDeclaration (MethodDefinition method)
4397 if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
4398 ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
4401 // Special signature for destructors.
4402 if (method.Name == "Finalize" && method.Parameters.Count == 0)
4403 return GetFinalizerName (method);
4405 StringBuilder buf = new StringBuilder ();
4407 AppendVisibility (buf, method);
4408 if (buf.Length == 0 &&
4409 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
4412 AppendModifiers (buf, method);
4414 if (buf.Length != 0)
4416 buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
4418 AppendMethodName (buf, method);
4419 AppendGenericMethod (buf, method).Append (" ");
4420 AppendParameters (buf, method, method.Parameters);
4421 AppendGenericMethodConstraints (buf, method);
4422 return buf.ToString ();
4425 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4427 return buf.Append (method.Name);
4430 protected virtual string GetFinalizerName (MethodDefinition method)
4435 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4440 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4445 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4450 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4455 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4460 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
4462 return GetPropertyName (property);
4465 protected virtual string GetFieldDeclaration (FieldDefinition field)
4467 return GetFieldName (field);
4470 protected virtual string GetEventDeclaration (EventDefinition e)
4472 return GetEventName (e);
4476 class ILFullMemberFormatter : MemberFormatter {
4478 public override string Language {
4479 get {return "ILAsm";}
4482 protected override char NestedTypeSeparator {
4488 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4490 if (GetBuiltinType (type.FullName) != null)
4492 string ns = DocUtils.GetNamespace (type);
4493 if (ns != null && ns.Length > 0) {
4494 if (type.IsValueType)
4495 buf.Append ("valuetype ");
4497 buf.Append ("class ");
4498 buf.Append (ns).Append ('.');
4503 protected static string GetBuiltinType (string t)
4506 case "System.Byte": return "unsigned int8";
4507 case "System.SByte": return "int8";
4508 case "System.Int16": return "int16";
4509 case "System.Int32": return "int32";
4510 case "System.Int64": return "int64";
4511 case "System.IntPtr": return "native int";
4513 case "System.UInt16": return "unsigned int16";
4514 case "System.UInt32": return "unsigned int32";
4515 case "System.UInt64": return "unsigned int64";
4516 case "System.UIntPtr": return "native unsigned int";
4518 case "System.Single": return "float32";
4519 case "System.Double": return "float64";
4520 case "System.Boolean": return "bool";
4521 case "System.Char": return "char";
4522 case "System.Void": return "void";
4523 case "System.String": return "string";
4524 case "System.Object": return "object";
4529 protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
4531 return buf.Append (typename);
4534 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4536 if (type is GenericParameter) {
4537 AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
4541 string s = GetBuiltinType (type.FullName);
4543 return buf.Append (s);
4545 return base.AppendTypeName (buf, type, context);
4548 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
4550 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
4551 return buf.Append (type.Owner is TypeReference ? "!" : "!!");
4553 GenericParameterAttributes attrs = type.Attributes;
4554 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
4555 buf.Append ("class ");
4556 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
4557 buf.Append ("struct ");
4558 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
4559 buf.Append (".ctor ");
4560 IList<TypeReference> constraints = type.Constraints;
4561 MemberFormatterState = 0;
4562 if (constraints.Count > 0) {
4563 var full = new ILFullMemberFormatter ();
4564 buf.Append ("(").Append (full.GetName (constraints [0]));
4565 for (int i = 1; i < constraints.Count; ++i) {
4566 buf.Append (", ").Append (full.GetName (constraints [i]));
4570 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4572 if ((attrs & GenericParameterAttributes.Covariant) != 0)
4574 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
4579 protected override string GetTypeDeclaration (TypeDefinition type)
4581 string visibility = GetTypeVisibility (type.Attributes);
4582 if (visibility == null)
4585 StringBuilder buf = new StringBuilder ();
4587 buf.Append (".class ");
4589 buf.Append ("nested ");
4590 buf.Append (visibility).Append (" ");
4591 if (type.IsInterface)
4592 buf.Append ("interface ");
4593 if (type.IsSequentialLayout)
4594 buf.Append ("sequential ");
4595 if (type.IsAutoLayout)
4596 buf.Append ("auto ");
4597 if (type.IsAnsiClass)
4598 buf.Append ("ansi ");
4599 if (type.IsAbstract)
4600 buf.Append ("abstract ");
4601 if (type.IsSerializable)
4602 buf.Append ("serializable ");
4604 buf.Append ("sealed ");
4605 if (type.IsBeforeFieldInit)
4606 buf.Append ("beforefieldinit ");
4607 var state = MemberFormatterState;
4608 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4609 buf.Append (GetName (type));
4610 MemberFormatterState = state;
4611 var full = new ILFullMemberFormatter ();
4612 if (type.BaseType != null) {
4613 buf.Append (" extends ");
4614 if (type.BaseType.FullName == "System.Object")
4615 buf.Append ("System.Object");
4617 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
4620 foreach (var name in type.Interfaces.Where (i => MDocUpdater.IsPublic (i.InterfaceType.Resolve ()))
4621 .Select (i => full.GetName (i.InterfaceType))
4622 .OrderBy (n => n)) {
4624 buf.Append (" implements ");
4633 return buf.ToString ();
4636 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4638 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4639 type is GenericInstanceType ? type.GetElementType () : type);
4641 foreach (var decl in decls) {
4642 TypeReference declDef = decl.Resolve () ?? decl;
4644 buf.Append (NestedTypeSeparator);
4647 AppendTypeName (buf, declDef, context);
4651 foreach (TypeReference arg in GetGenericArguments (type)) {
4655 _AppendTypeName (buf, arg, context);
4661 static string GetTypeVisibility (TypeAttributes ta)
4663 switch (ta & TypeAttributes.VisibilityMask) {
4664 case TypeAttributes.Public:
4665 case TypeAttributes.NestedPublic:
4668 case TypeAttributes.NestedFamily:
4669 case TypeAttributes.NestedFamORAssem:
4677 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4679 return GetMethodDeclaration (constructor);
4682 protected override string GetMethodDeclaration (MethodDefinition method)
4684 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
4687 var buf = new StringBuilder ();
4688 buf.Append (".method ");
4689 AppendVisibility (buf, method);
4690 if (method.IsStatic)
4691 buf.Append ("static ");
4692 if (method.IsHideBySig)
4693 buf.Append ("hidebysig ");
4694 if (method.IsPInvokeImpl) {
4695 var info = method.PInvokeInfo;
4696 buf.Append ("pinvokeimpl (\"")
4697 .Append (info.Module.Name)
4698 .Append ("\" as \"")
4699 .Append (info.EntryPoint)
4701 if (info.IsCharSetAuto)
4702 buf.Append (" auto");
4703 if (info.IsCharSetUnicode)
4704 buf.Append (" unicode");
4705 if (info.IsCharSetAnsi)
4706 buf.Append (" ansi");
4707 if (info.IsCallConvCdecl)
4708 buf.Append (" cdecl");
4709 if (info.IsCallConvStdCall)
4710 buf.Append (" stdcall");
4711 if (info.IsCallConvWinapi)
4712 buf.Append (" winapi");
4713 if (info.IsCallConvThiscall)
4714 buf.Append (" thiscall");
4715 if (info.SupportsLastError)
4716 buf.Append (" lasterr");
4719 if (method.IsSpecialName)
4720 buf.Append ("specialname ");
4721 if (method.IsRuntimeSpecialName)
4722 buf.Append ("rtspecialname ");
4723 if (method.IsNewSlot)
4724 buf.Append ("newslot ");
4725 if (method.IsVirtual)
4726 buf.Append ("virtual ");
4727 if (!method.IsStatic)
4728 buf.Append ("instance ");
4729 _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
4731 .Append (method.Name);
4732 if (method.IsGenericMethod ()) {
4733 var state = MemberFormatterState;
4734 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4735 IList<GenericParameter> args = method.GenericParameters;
4736 if (args.Count > 0) {
4738 _AppendTypeName (buf, args [0], null);
4739 for (int i = 1; i < args.Count; ++i)
4740 _AppendTypeName (buf.Append (", "), args [i], null);
4743 MemberFormatterState = state;
4748 for (int i = 0; i < method.Parameters.Count; ++i) {
4752 _AppendTypeName (buf, method.Parameters [i].ParameterType, new DynamicParserContext (method.Parameters [i]));
4754 buf.Append (method.Parameters [i].Name);
4758 buf.Append (" cil");
4759 if (method.IsRuntime)
4760 buf.Append (" runtime");
4761 if (method.IsManaged)
4762 buf.Append (" managed");
4764 return buf.ToString ();
4767 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4769 if (DocUtils.IsExplicitlyImplemented (method)) {
4770 TypeReference iface;
4771 MethodReference ifaceMethod;
4772 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4773 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4775 .Append (ifaceMethod.Name);
4777 return base.AppendMethodName (buf, method);
4780 protected override string RefTypeModifier {
4784 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4786 if (method.IsPublic)
4787 return buf.Append ("public ");
4788 if (method.IsFamilyAndAssembly)
4789 return buf.Append ("familyandassembly");
4790 if (method.IsFamilyOrAssembly)
4791 return buf.Append ("familyorassembly");
4792 if (method.IsFamily)
4793 return buf.Append ("family");
4797 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4799 string modifiers = String.Empty;
4800 if (method.IsStatic) modifiers += " static";
4801 if (method.IsVirtual && !method.IsAbstract) {
4802 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4803 else modifiers += " override";
4805 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4806 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4807 if (method.IsFinal) modifiers += " sealed";
4808 if (modifiers == " virtual sealed") modifiers = "";
4810 return buf.Append (modifiers);
4813 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4815 if (method.IsGenericMethod ()) {
4816 IList<GenericParameter> args = method.GenericParameters;
4817 if (args.Count > 0) {
4819 buf.Append (args [0].Name);
4820 for (int i = 1; i < args.Count; ++i)
4821 buf.Append (",").Append (args [i].Name);
4828 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4830 return AppendParameters (buf, method, parameters, '(', ')');
4833 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4837 if (parameters.Count > 0) {
4838 if (DocUtils.IsExtensionMethod (method))
4839 buf.Append ("this ");
4840 AppendParameter (buf, parameters [0]);
4841 for (int i = 1; i < parameters.Count; ++i) {
4843 AppendParameter (buf, parameters [i]);
4847 return buf.Append (end);
4850 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4852 if (parameter.ParameterType is ByReferenceType) {
4853 if (parameter.IsOut)
4854 buf.Append ("out ");
4856 buf.Append ("ref ");
4858 buf.Append (GetName (parameter.ParameterType)).Append (" ");
4859 return buf.Append (parameter.Name);
4862 protected override string GetPropertyDeclaration (PropertyDefinition property)
4864 MethodDefinition gm = null, sm = null;
4866 string get_visible = null;
4867 if ((gm = property.GetMethod) != null &&
4868 (DocUtils.IsExplicitlyImplemented (gm) ||
4869 (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
4870 get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
4871 string set_visible = null;
4872 if ((sm = property.SetMethod) != null &&
4873 (DocUtils.IsExplicitlyImplemented (sm) ||
4874 (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
4875 set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
4877 if ((set_visible == null) && (get_visible == null))
4880 StringBuilder buf = new StringBuilder ()
4881 .Append (".property ");
4882 if (!(gm ?? sm).IsStatic)
4883 buf.Append ("instance ");
4884 _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
4885 buf.Append (' ').Append (property.Name);
4886 if (!property.HasParameters || property.Parameters.Count == 0)
4887 return buf.ToString ();
4891 foreach (ParameterDefinition p in property.Parameters) {
4895 _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
4899 return buf.ToString ();
4902 protected override string GetFieldDeclaration (FieldDefinition field)
4904 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4905 if (declType.IsEnum && field.Name == "value__")
4906 return null; // This member of enums aren't documented.
4908 StringBuilder buf = new StringBuilder ();
4909 AppendFieldVisibility (buf, field);
4910 if (buf.Length == 0)
4913 buf.Insert (0, ".field ");
4916 buf.Append ("static ");
4917 if (field.IsInitOnly)
4918 buf.Append ("initonly ");
4919 if (field.IsLiteral)
4920 buf.Append ("literal ");
4921 _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
4922 buf.Append (' ').Append (field.Name);
4923 AppendFieldValue (buf, field);
4925 return buf.ToString ();
4928 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4931 return buf.Append ("public ");
4932 if (field.IsFamilyAndAssembly)
4933 return buf.Append ("familyandassembly ");
4934 if (field.IsFamilyOrAssembly)
4935 return buf.Append ("familyorassembly ");
4937 return buf.Append ("family ");
4941 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4943 // enums have a value__ field, which we ignore
4944 if (field.DeclaringType.IsGenericType ())
4946 if (field.HasConstant && field.IsLiteral) {
4949 val = field.Constant;
4954 buf.Append (" = ").Append ("null");
4955 else if (val is Enum)
4957 .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4959 .Append (val.ToString ())
4961 else if (val is IFormattable) {
4962 string value = ((IFormattable)val).ToString(null, CultureInfo.InvariantCulture);
4965 buf.Append ("\"" + value + "\"");
4967 buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4976 protected override string GetEventDeclaration (EventDefinition e)
4978 StringBuilder buf = new StringBuilder ();
4979 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4984 buf.Append (".event ")
4985 .Append (GetName (e.EventType))
4989 return buf.ToString ();
4993 class ILMemberFormatter : ILFullMemberFormatter {
4994 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5000 class ILNativeTypeMemberFormatter : ILFullMemberFormatter {
5001 protected static string _GetBuiltinType (string t)
5003 //string moddedType = base.GetBuiltinType (t);
5005 //return moddedType;
5009 class CSharpNativeTypeMemberFormatter : CSharpFullMemberFormatter {
5010 protected override string GetCSharpType (string t) {
5011 string moddedType = base.GetCSharpType (t);
5013 switch (moddedType) {
5014 case "int": return "nint";
5019 case "System.Drawing.SizeF":
5020 return "CoreGraphics.CGSize";
5021 case "System.Drawing.PointF":
5022 return "CoreGraphics.CGPoint";
5023 case "System.Drawing.RectangleF":
5024 return "CoreGraphics.CGPoint";
5030 class CSharpFullMemberFormatter : MemberFormatter {
5032 public override string Language {
5036 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5039 string ns = DocUtils.GetNamespace (type);
5040 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
5041 buf.Append (ns).Append ('.');
5045 protected virtual string GetCSharpType (string t)
5048 case "System.Byte": return "byte";
5049 case "System.SByte": return "sbyte";
5050 case "System.Int16": return "short";
5051 case "System.Int32": return "int";
5052 case "System.Int64": return "long";
5054 case "System.UInt16": return "ushort";
5055 case "System.UInt32": return "uint";
5056 case "System.UInt64": return "ulong";
5058 case "System.Single": return "float";
5059 case "System.Double": return "double";
5060 case "System.Decimal": return "decimal";
5061 case "System.Boolean": return "bool";
5062 case "System.Char": return "char";
5063 case "System.Void": return "void";
5064 case "System.String": return "string";
5065 case "System.Object": return "object";
5070 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
5072 if (context != null && context.TransformFlags != null &&
5073 (context.TransformFlags.Count == 0 || context.TransformFlags [context.TransformIndex])) {
5074 context.TransformIndex++;
5075 return buf.Append ("dynamic");
5078 if (type is GenericParameter)
5079 return AppendGenericParameterConstraints (buf, (GenericParameter) type, context).Append (type.Name);
5080 string t = type.FullName;
5081 if (!t.StartsWith ("System.")) {
5082 return base.AppendTypeName (buf, type, context);
5085 string s = GetCSharpType (t);
5087 if (context != null)
5088 context.TransformIndex++;
5089 return buf.Append (s);
5092 return base.AppendTypeName (buf, type, context);
5095 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
5097 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
5099 GenericParameterAttributes attrs = type.Attributes;
5100 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
5101 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
5105 buf.Append ("out ");
5109 protected override string GetTypeDeclaration (TypeDefinition type)
5111 string visibility = GetTypeVisibility (type.Attributes);
5112 if (visibility == null)
5115 StringBuilder buf = new StringBuilder ();
5117 buf.Append (visibility);
5120 MemberFormatter full = new CSharpFullMemberFormatter ();
5122 if (DocUtils.IsDelegate (type)) {
5123 buf.Append("delegate ");
5124 MethodDefinition invoke = type.GetMethod ("Invoke");
5125 buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
5126 buf.Append (GetName (type));
5127 AppendParameters (buf, invoke, invoke.Parameters);
5128 AppendGenericTypeConstraints (buf, type);
5131 return buf.ToString();
5134 if (type.IsAbstract && !type.IsInterface)
5135 buf.Append("abstract ");
5136 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
5137 buf.Append("sealed ");
5138 buf.Replace ("abstract sealed", "static");
5140 buf.Append (GetTypeKind (type));
5142 buf.Append (GetCSharpType (type.FullName) == null
5147 TypeReference basetype = type.BaseType;
5148 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
5151 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
5152 .Select (iface => full.GetName (iface))
5156 if (basetype != null || interface_names.Count > 0)
5159 if (basetype != null) {
5160 buf.Append (full.GetName (basetype));
5161 if (interface_names.Count > 0)
5165 for (int i = 0; i < interface_names.Count; i++){
5168 buf.Append (interface_names [i]);
5170 AppendGenericTypeConstraints (buf, type);
5173 return buf.ToString ();
5176 static string GetTypeKind (TypeDefinition t)
5182 if (t.IsClass || t.FullName == "System.Enum")
5186 throw new ArgumentException(t.FullName);
5189 static string GetTypeVisibility (TypeAttributes ta)
5191 switch (ta & TypeAttributes.VisibilityMask) {
5192 case TypeAttributes.Public:
5193 case TypeAttributes.NestedPublic:
5196 case TypeAttributes.NestedFamily:
5197 case TypeAttributes.NestedFamORAssem:
5205 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
5207 if (type.GenericParameters.Count == 0)
5209 return AppendConstraints (buf, type.GenericParameters);
5212 private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
5214 foreach (GenericParameter genArg in genArgs) {
5215 GenericParameterAttributes attrs = genArg.Attributes;
5216 IList<TypeReference> constraints = genArg.Constraints;
5217 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
5220 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
5221 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
5222 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
5225 if (!isref && !isvt && !isnew && constraints.Count == 0)
5227 buf.Append (" where ").Append (genArg.Name).Append (" : ");
5229 buf.Append ("class");
5233 buf.Append ("struct");
5236 if (constraints.Count > 0 && !isvt) {
5239 buf.Append (GetTypeName (constraints [0]));
5240 for (int i = 1; i < constraints.Count; ++i)
5241 buf.Append (", ").Append (GetTypeName (constraints [i]));
5243 if (isnew && !isvt) {
5246 buf.Append ("new()");
5252 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5254 StringBuilder buf = new StringBuilder ();
5255 AppendVisibility (buf, constructor);
5256 if (buf.Length == 0)
5260 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
5261 AppendParameters (buf, constructor, constructor.Parameters);
5264 return buf.ToString ();
5267 protected override string GetMethodDeclaration (MethodDefinition method)
5269 string decl = base.GetMethodDeclaration (method);
5275 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
5277 if (DocUtils.IsExplicitlyImplemented (method)) {
5278 TypeReference iface;
5279 MethodReference ifaceMethod;
5280 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5281 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
5283 .Append (ifaceMethod.Name);
5285 return base.AppendMethodName (buf, method);
5288 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
5290 if (method.GenericParameters.Count == 0)
5292 return AppendConstraints (buf, method.GenericParameters);
5295 protected override string RefTypeModifier {
5299 protected override string GetFinalizerName (MethodDefinition method)
5301 return "~" + method.DeclaringType.Name + " ()";
5304 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
5308 if (method.IsPublic)
5309 return buf.Append ("public");
5310 if (method.IsFamily || method.IsFamilyOrAssembly)
5311 return buf.Append ("protected");
5315 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
5317 string modifiers = String.Empty;
5318 if (method.IsStatic) modifiers += " static";
5319 if (method.IsVirtual && !method.IsAbstract) {
5320 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
5321 else modifiers += " override";
5323 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
5324 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
5325 if (method.IsFinal) modifiers += " sealed";
5326 if (modifiers == " virtual sealed") modifiers = "";
5328 return buf.Append (modifiers);
5331 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
5333 if (method.IsGenericMethod ()) {
5334 IList<GenericParameter> args = method.GenericParameters;
5335 if (args.Count > 0) {
5337 buf.Append (args [0].Name);
5338 for (int i = 1; i < args.Count; ++i)
5339 buf.Append (",").Append (args [i].Name);
5346 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
5348 return AppendParameters (buf, method, parameters, '(', ')');
5351 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
5355 if (parameters.Count > 0) {
5356 if (DocUtils.IsExtensionMethod (method))
5357 buf.Append ("this ");
5358 AppendParameter (buf, parameters [0]);
5359 for (int i = 1; i < parameters.Count; ++i) {
5361 AppendParameter (buf, parameters [i]);
5365 return buf.Append (end);
5368 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
5370 if (parameter.ParameterType is ByReferenceType) {
5371 if (parameter.IsOut)
5372 buf.Append ("out ");
5374 buf.Append ("ref ");
5376 buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
5377 buf.Append (parameter.Name);
5378 if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant) {
5379 buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
5384 protected override string GetPropertyDeclaration (PropertyDefinition property)
5386 MethodDefinition method;
5388 string get_visible = null;
5389 if ((method = property.GetMethod) != null &&
5390 (DocUtils.IsExplicitlyImplemented (method) ||
5391 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5392 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5393 string set_visible = null;
5394 if ((method = property.SetMethod) != null &&
5395 (DocUtils.IsExplicitlyImplemented (method) ||
5396 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5397 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5399 if ((set_visible == null) && (get_visible == null))
5403 StringBuilder buf = new StringBuilder ();
5404 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
5405 buf.Append (visibility = get_visible);
5406 else if (set_visible != null && get_visible == null)
5407 buf.Append (visibility = set_visible);
5409 buf.Append (visibility = "public");
5411 // Pick an accessor to use for static/virtual/override/etc. checks.
5412 method = property.SetMethod;
5414 method = property.GetMethod;
5416 string modifiers = String.Empty;
5417 if (method.IsStatic) modifiers += " static";
5418 if (method.IsVirtual && !method.IsAbstract) {
5419 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
5420 modifiers += " virtual";
5422 modifiers += " override";
5424 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
5425 if (method.IsAbstract && !declDef.IsInterface)
5426 modifiers += " abstract";
5428 modifiers += " sealed";
5429 if (modifiers == " virtual sealed")
5431 buf.Append (modifiers).Append (' ');
5433 buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
5435 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
5436 string name = property.Name;
5437 foreach (MemberReference mi in defs) {
5438 if (mi == property) {
5443 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
5445 if (property.Parameters.Count != 0) {
5446 AppendParameters (buf, method, property.Parameters, '[', ']');
5450 if (get_visible != null) {
5451 if (get_visible != visibility)
5452 buf.Append (' ').Append (get_visible);
5453 buf.Append (" get;");
5455 if (set_visible != null) {
5456 if (set_visible != visibility)
5457 buf.Append (' ').Append (set_visible);
5458 buf.Append (" set;");
5462 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
5465 protected override string GetFieldDeclaration (FieldDefinition field)
5467 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
5468 if (declType.IsEnum && field.Name == "value__")
5469 return null; // This member of enums aren't documented.
5471 StringBuilder buf = new StringBuilder ();
5472 AppendFieldVisibility (buf, field);
5473 if (buf.Length == 0)
5476 if (declType.IsEnum)
5479 if (field.IsStatic && !field.IsLiteral)
5480 buf.Append (" static");
5481 if (field.IsInitOnly)
5482 buf.Append (" readonly");
5483 if (field.IsLiteral)
5484 buf.Append (" const");
5486 buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
5487 buf.Append (field.Name);
5488 AppendFieldValue (buf, field);
5491 return buf.ToString ();
5494 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
5497 return buf.Append ("public");
5498 if (field.IsFamily || field.IsFamilyOrAssembly)
5499 return buf.Append ("protected");
5503 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
5505 // enums have a value__ field, which we ignore
5506 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
5507 field.DeclaringType.IsGenericType ())
5509 if (field.HasConstant && field.IsLiteral) {
5512 val = field.Constant;
5517 buf.Append (" = ").Append ("null");
5518 else if (val is Enum)
5519 buf.Append (" = ").Append (val.ToString ());
5520 else if (val is IFormattable) {
5521 string value = ((IFormattable)val).ToString(null, CultureInfo.InvariantCulture);
5523 value = "\"" + value + "\"";
5524 buf.Append (" = ").Append (value);
5530 protected override string GetEventDeclaration (EventDefinition e)
5532 StringBuilder buf = new StringBuilder ();
5533 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
5537 AppendModifiers (buf, e.AddMethod);
5539 buf.Append (" event ");
5540 buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters [0]))).Append (' ');
5541 buf.Append (e.Name).Append (';');
5543 return buf.ToString ();
5547 class CSharpMemberFormatter : CSharpFullMemberFormatter {
5548 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5554 class DocTypeFullMemberFormatter : MemberFormatter {
5555 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
5557 protected override char NestedTypeSeparator {
5562 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
5563 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5569 class SlashDocMemberFormatter : MemberFormatter {
5571 protected override char[] GenericTypeContainer {
5572 get {return new char[]{'{', '}'};}
5575 private bool AddTypeCount = true;
5577 private TypeReference genDeclType;
5578 private MethodReference genDeclMethod;
5580 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
5582 if (type is GenericParameter) {
5584 if (genDeclType != null) {
5585 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
5586 for (int i = 0; i < genArgs.Count; ++i) {
5587 if (genArgs [i].Name == type.Name) {
5588 buf.Append ('`').Append (i);
5593 if (genDeclMethod != null) {
5594 IList<GenericParameter> genArgs = null;
5595 if (genDeclMethod.IsGenericMethod ()) {
5596 genArgs = genDeclMethod.GenericParameters;
5597 for (int i = 0; i < genArgs.Count; ++i) {
5598 if (genArgs [i].Name == type.Name) {
5599 buf.Append ("``").Append (i);
5605 if (genDeclType == null && genDeclMethod == null) {
5606 // Probably from within an explicitly implemented interface member,
5607 // where CSC uses parameter names instead of indices (why?), e.g.
5608 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
5609 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
5610 buf.Append (type.Name);
5612 if (buf.Length == l) {
5613 throw new Exception (string.Format (
5614 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
5615 type.Name, genDeclType, genDeclMethod));
5619 base.AppendTypeName (buf, type, context);
5621 int numArgs = type.GenericParameters.Count;
5622 if (type.DeclaringType != null)
5623 numArgs -= type.GenericParameters.Count;
5625 buf.Append ('`').Append (numArgs);
5632 protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
5634 buf.Append (ArrayDelimeters [0]);
5635 int rank = array.Rank;
5638 for (int i = 1; i < rank; ++i) {
5642 return buf.Append (ArrayDelimeters [1]);
5645 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5648 base.AppendGenericType (buf, type, context);
5650 AppendType (buf, type, context);
5654 private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5656 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
5657 bool insertNested = false;
5658 int prevParamCount = 0;
5659 foreach (var decl in decls) {
5661 buf.Append (NestedTypeSeparator);
5662 insertNested = true;
5663 base.AppendTypeName (buf, decl, context);
5664 int argCount = DocUtils.GetGenericArgumentCount (decl);
5665 int numArgs = argCount - prevParamCount;
5666 prevParamCount = argCount;
5668 buf.Append ('`').Append (numArgs);
5673 protected override string GetConstructorName (MethodReference constructor)
5675 return GetMethodDefinitionName (constructor, "#ctor");
5678 protected override string GetMethodName (MethodReference method)
5681 MethodDefinition methodDef = method as MethodDefinition;
5682 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
5685 TypeReference iface;
5686 MethodReference ifaceMethod;
5687 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
5688 AddTypeCount = false;
5689 name = GetTypeName (iface) + "." + ifaceMethod.Name;
5690 AddTypeCount = true;
5692 return GetMethodDefinitionName (method, name);
5695 private string GetMethodDefinitionName (MethodReference method, string name)
5697 StringBuilder buf = new StringBuilder ();
5698 buf.Append (GetTypeName (method.DeclaringType));
5700 buf.Append (name.Replace (".", "#"));
5701 if (method.IsGenericMethod ()) {
5702 IList<GenericParameter> genArgs = method.GenericParameters;
5703 if (genArgs.Count > 0)
5704 buf.Append ("``").Append (genArgs.Count);
5706 IList<ParameterDefinition> parameters = method.Parameters;
5708 genDeclType = method.DeclaringType;
5709 genDeclMethod = method;
5710 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
5714 genDeclMethod = null;
5716 return buf.ToString ();
5719 private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
5721 if (parameters.Count == 0)
5726 AppendParameter (buf, genArgs, parameters [0]);
5727 for (int i = 1; i < parameters.Count; ++i) {
5729 AppendParameter (buf, genArgs, parameters [i]);
5732 return buf.Append (')');
5735 private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
5737 AddTypeCount = false;
5738 buf.Append (GetTypeName (parameter.ParameterType));
5739 AddTypeCount = true;
5743 protected override string GetPropertyName (PropertyReference property)
5747 PropertyDefinition propertyDef = property as PropertyDefinition;
5748 MethodDefinition method = null;
5749 if (propertyDef != null)
5750 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
5751 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
5752 name = property.Name;
5754 TypeReference iface;
5755 MethodReference ifaceMethod;
5756 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5757 AddTypeCount = false;
5758 name = string.Join ("#", new string[]{
5759 GetTypeName (iface).Replace (".", "#"),
5760 DocUtils.GetMember (property.Name)
5762 AddTypeCount = true;
5765 StringBuilder buf = new StringBuilder ();
5766 buf.Append (GetName (property.DeclaringType));
5769 IList<ParameterDefinition> parameters = property.Parameters;
5770 if (parameters.Count > 0) {
5771 genDeclType = property.DeclaringType;
5773 IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
5774 AppendParameter (buf, genArgs, parameters [0]);
5775 for (int i = 1; i < parameters.Count; ++i) {
5777 AppendParameter (buf, genArgs, parameters [i]);
5782 return buf.ToString ();
5785 protected override string GetFieldName (FieldReference field)
5787 return string.Format ("{0}.{1}",
5788 GetName (field.DeclaringType), field.Name);
5791 protected override string GetEventName (EventReference e)
5793 return string.Format ("{0}.{1}",
5794 GetName (e.DeclaringType), e.Name);
5797 protected override string GetTypeDeclaration (TypeDefinition type)
5799 string name = GetName (type);
5805 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5807 string name = GetName (constructor);
5813 protected override string GetMethodDeclaration (MethodDefinition method)
5815 string name = GetName (method);
5818 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
5819 genDeclType = method.DeclaringType;
5820 genDeclMethod = method;
5821 name += "~" + GetName (method.ReturnType);
5823 genDeclMethod = null;
5828 protected override string GetPropertyDeclaration (PropertyDefinition property)
5830 string name = GetName (property);
5836 protected override string GetFieldDeclaration (FieldDefinition field)
5838 string name = GetName (field);
5844 protected override string GetEventDeclaration (EventDefinition e)
5846 string name = GetName (e);
5853 class FileNameMemberFormatter : SlashDocMemberFormatter {
5854 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5859 protected override char NestedTypeSeparator {
5864 class ResolvedTypeInfo {
5865 TypeDefinition typeDef;
5867 public ResolvedTypeInfo (TypeReference value) {
5871 public TypeReference Reference { get; private set; }
5873 public TypeDefinition Definition {
5875 if (typeDef == null) {
5876 typeDef = Reference.Resolve ();
5883 /// <summary>Formats attribute values. Should return true if it is able to format the value.</summary>
5884 class AttributeValueFormatter {
5885 public virtual bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5887 TypeReference valueType = type.Reference;
5889 returnvalue = "null";
5892 if (valueType.FullName == "System.Type") {
5893 var vTypeRef = v as TypeReference;
5894 if (vTypeRef != null)
5895 returnvalue = "typeof(" + NativeTypeManager.GetTranslatedName (vTypeRef) + ")"; // TODO: drop NS handling
5897 returnvalue = "typeof(" + v.ToString () + ")";
5901 if (valueType.FullName == "System.String") {
5902 returnvalue = "\"" + v.ToString () + "\"";
5905 if (valueType.FullName == "System.Char") {
5906 returnvalue = "'" + v.ToString () + "'";
5910 returnvalue = (bool)v ? "true" : "false";
5914 TypeDefinition valueDef = type.Definition;
5915 if (valueDef == null || !valueDef.IsEnum) {
5916 returnvalue = v.ToString ();
5920 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5921 var values = MDocUpdater.GetEnumerationValues (valueDef);
5922 long c = MDocUpdater.ToInt64 (v);
5923 if (values.ContainsKey (c)) {
5924 returnvalue = typename + "." + values [c];
5933 /// <summary>The final value formatter in the pipeline ... if no other formatter formats the value,
5934 /// then this one will serve as the default implementation.</summary>
5935 class DefaultAttributeValueFormatter : AttributeValueFormatter {
5936 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5938 returnvalue = "(" + MDocUpdater.GetDocTypeFullName (type.Reference) + ") " + v.ToString ();
5943 /// <summary>Flags enum formatter that assumes powers of two values.</summary>
5944 /// <remarks>As described here: https://msdn.microsoft.com/en-us/library/vstudio/ms229062(v=vs.100).aspx</remarks>
5945 class StandardFlagsEnumFormatter : AttributeValueFormatter {
5946 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5948 TypeReference valueType = type.Reference;
5949 TypeDefinition valueDef = type.Definition;
5950 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
5952 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5953 var values = MDocUpdater.GetEnumerationValues (valueDef);
5954 long c = MDocUpdater.ToInt64 (v);
5955 returnvalue = string.Join (" | ",
5956 (from i in values.Keys
5957 where (c & i) == i && i != 0
5958 select typename + "." + values [i])
5959 .DefaultIfEmpty (c.ToString ()).ToArray ());
5969 /// <summary>A custom formatter for the ObjCRuntime.Platform enumeration.</summary>
5970 class ApplePlatformEnumFormatter : AttributeValueFormatter {
5971 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5973 TypeReference valueType = type.Reference;
5974 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5975 TypeDefinition valueDef = type.Definition;
5976 if (typename.Contains ("ObjCRuntime.Platform") && valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
5978 var values = MDocUpdater.GetEnumerationValues (valueDef);
5979 long c = MDocUpdater.ToInt64 (v);
5981 returnvalue = Format (c, values, typename);
5989 string Format (long c, IDictionary<long, string> values, string typename)
5991 int iosarch, iosmajor, iosminor, iossubminor;
5992 int macarch, macmajor, macminor, macsubminor;
5993 GetEncodingiOS (c, out iosarch, out iosmajor, out iosminor, out iossubminor);
5994 GetEncodingMac ((ulong)c, out macarch, out macmajor, out macminor, out macsubminor);
5996 if (iosmajor == 0 & iosminor == 0 && iossubminor == 0) {
5997 return FormatValues ("Mac", macarch, macmajor, macminor, macsubminor);
6000 if (macmajor == 0 & macminor == 0 && macsubminor == 0) {
6001 return FormatValues ("iOS", iosarch, iosmajor, iosminor, iossubminor);
6004 return string.Format ("(Platform){0}", c);
6007 string FormatValues (string plat, int arch, int major, int minor, int subminor)
6009 string archstring = "";
6018 return string.Format ("Platform.{4}_{0}_{1}{2} | Platform.{4}_Arch{3}",
6021 subminor == 0 ? "" : "_" + subminor.ToString (),
6027 void GetEncodingiOS (long entireLong, out int archindex, out int major, out int minor, out int subminor)
6029 long lowerBits = entireLong & 0xffffffff;
6030 int lowerBitsAsInt = (int) lowerBits;
6031 GetEncoding (lowerBitsAsInt, out archindex, out major, out minor, out subminor);
6034 void GetEncodingMac (ulong entireLong, out int archindex, out int major, out int minor, out int subminor)
6036 ulong higherBits = entireLong & 0xffffffff00000000;
6037 int higherBitsAsInt = (int) ((higherBits) >> 32);
6038 GetEncoding (higherBitsAsInt, out archindex, out major, out minor, out subminor);
6041 void GetEncoding (Int32 encodedBits, out int archindex, out int major, out int minor, out int subminor)
6043 // format is AAJJNNSS
6044 archindex = (int)((encodedBits & 0xFF000000) >> 24);
6045 major = (int)((encodedBits & 0x00FF0000) >> 16);
6046 minor = (int)((encodedBits & 0x0000FF00) >> 8);
6047 subminor = (int)((encodedBits & 0x000000FF) >> 0);