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();
137 bool show_exceptions;
138 bool no_assembly_versions, ignore_missing_types;
139 ExceptionLocations? exceptions;
141 internal int additions = 0, deletions = 0;
143 List<DocumentationImporter> importers = new List<DocumentationImporter> ();
145 DocumentationEnumerator docEnum;
149 static readonly MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
150 static readonly MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
152 static MemberFormatter[] typeFormatters = new MemberFormatter[]{
153 new CSharpMemberFormatter (),
154 new ILMemberFormatter (),
157 static MemberFormatter[] memberFormatters = new MemberFormatter[]{
158 new CSharpFullMemberFormatter (),
159 new ILFullMemberFormatter (),
162 internal static readonly MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
164 MyXmlNodeList extensionMethods = new MyXmlNodeList ();
166 HashSet<string> forwardedTypes = new HashSet<string> ();
168 public static string droppedNamespace = string.Empty;
170 public static bool HasDroppedNamespace(TypeDefinition forType)
172 return HasDroppedNamespace(forType.Module);
175 public static bool HasDroppedNamespace(MemberReference forMember)
177 return HasDroppedNamespace(forMember.Module);
180 public static bool HasDroppedNamespace(AssemblyDefinition forAssembly)
182 return HasDroppedNamespace(forAssembly.MainModule);
185 public static bool HasDroppedNamespace(ModuleDefinition forModule)
187 return !string.IsNullOrWhiteSpace (droppedNamespace) && droppedAssemblies.Any(da => da == forModule.Name);
191 static List<string> droppedAssemblies = new List<string>();
193 public string PreserveTag { get; set; }
194 public static MDocUpdater Instance { get; private set; }
195 public static bool SwitchingToMagicTypes { get; private set; }
197 public override void Run (IEnumerable<string> args)
200 show_exceptions = DebugOutput;
201 var types = new List<string> ();
202 var p = new OptionSet () {
204 "Delete removed members from the XML files.",
205 v => delete = v != null },
207 "Document potential exceptions that members can generate. {SOURCES} " +
208 "is a comma-separated list of:\n" +
209 " asm Method calls in same assembly\n" +
210 " depasm Method calls in dependent assemblies\n" +
211 " all Record all possible exceptions\n" +
212 " added Modifier; only create <exception/>s\n" +
213 " for NEW types/members\n" +
214 "If nothing is specified, then only exceptions from the member will " +
216 v => exceptions = ParseExceptionLocations (v) },
218 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
221 case "ignore-missing-types":
222 ignore_missing_types = true;
224 case "no-assembly-versions":
225 no_assembly_versions = true;
228 throw new Exception ("Unsupported flag `" + v + "'.");
231 { "fignore-missing-types",
232 "Do not report an error if a --type=TYPE type\nwas not found.",
233 v => ignore_missing_types = v != null },
234 { "fno-assembly-versions",
235 "Do not generate //AssemblyVersion elements.",
236 v => no_assembly_versions = v != null },
238 "Import documentation from {FILE}.",
239 v => AddImporter (v) },
241 "Check for assembly references in {DIRECTORY}.",
242 v => assemblyResolver.AddSearchDirectory (v) },
244 "Ignored for compatibility with update-ecma-xml.",
247 "Root {DIRECTORY} to generate/update documentation.",
250 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
251 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
252 v => assemblyResolver.AddSearchDirectory (Path.GetDirectoryName (v)) },
254 "Manually specify the assembly {VERSION} that new members were added in.",
257 "Only update documentation for {TYPE}.",
258 v => types.Add (v) },
260 "When processing assembly {ASSEMBLY}, strip off leading namespace {PREFIX}:\n" +
261 " e.g. --dropns ASSEMBLY=PREFIX",
263 var parts = v.Split ('=');
264 if (parts.Length != 2) { Console.Error.WriteLine ("Invalid dropns input"); return; }
265 var assembly = Path.GetFileName (parts [0].Trim ());
266 var prefix = parts [1].Trim();
267 droppedAssemblies.Add (assembly);
268 droppedNamespace = prefix;
271 "If the new assembly is switching to 'magic types', then this switch should be defined.",
272 v => SwitchingToMagicTypes = true },
274 "Do not delete members that don't exist in the assembly, but rather mark them as preserved.",
275 v => PreserveTag = "true" },
277 var assemblies = Parse (p, args, "update",
278 "[OPTIONS]+ ASSEMBLIES",
279 "Create or update documentation from ASSEMBLIES.");
280 if (assemblies == null)
282 if (assemblies.Count == 0)
283 Error ("No assemblies specified.");
285 foreach (var dir in assemblies
286 .Where (a => a.Contains (Path.DirectorySeparatorChar))
287 .Select (a => Path.GetDirectoryName (a)))
288 assemblyResolver.AddSearchDirectory (dir);
290 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
293 throw new InvalidOperationException("The --out option is required.");
295 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
297 // Store types that have been forwarded to avoid duplicate generation
298 GatherForwardedTypes ();
300 docEnum = docEnum ?? new DocumentationEnumerator ();
302 // PERFORM THE UPDATES
304 if (types.Count > 0) {
306 DoUpdateTypes (srcPath, types, srcPath);
309 else if (opts.@namespace != null)
310 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
311 Path.Combine (dest_dir, opts.@namespace));
314 DoUpdateAssemblies (srcPath, srcPath);
316 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
318 public static bool IsInAssemblies(string name) {
319 var query = Instance.assemblies.Where (a => a.MainModule.Name == name).ToArray ();
320 return query.Length > 0;
322 void AddImporter (string path)
325 XmlReader r = new XmlTextReader (path);
327 while (r.NodeType != XmlNodeType.Element) {
329 Error ("Unable to read XML file: {0}.", path);
331 if (r.LocalName == "doc") {
332 importers.Add (new MsxdocDocumentationImporter (path));
334 else if (r.LocalName == "Libraries") {
335 var ecmadocs = new XmlTextReader (path);
336 docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
337 importers.Add (new EcmaDocumentationImporter (ecmadocs));
340 Error ("Unsupported XML format within {0}.", path);
343 } catch (Exception e) {
344 Environment.ExitCode = 1;
345 Error ("Could not load XML file: {0}.", e.Message);
349 void GatherForwardedTypes ()
351 foreach (var asm in assemblies)
352 foreach (var type in asm.MainModule.ExportedTypes.Where (t => t.IsForwarder).Select (t => t.FullName))
353 forwardedTypes.Add (type);
356 static ExceptionLocations ParseExceptionLocations (string s)
358 ExceptionLocations loc = ExceptionLocations.Member;
361 foreach (var type in s.Split (',')) {
363 case "added": loc |= ExceptionLocations.AddedMembers; break;
364 case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
365 case "asm": loc |= ExceptionLocations.Assembly; break;
366 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
367 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
373 internal void Warning (string format, params object[] args)
375 Message (TraceLevel.Warning, "mdoc: " + format, args);
378 private AssemblyDefinition LoadAssembly (string name)
380 AssemblyDefinition assembly = null;
382 assembly = AssemblyDefinition.ReadAssembly (name, new ReaderParameters { AssemblyResolver = assemblyResolver });
383 } catch (System.IO.FileNotFoundException) { }
385 if (assembly == null)
386 throw new InvalidOperationException("Assembly " + name + " not found.");
391 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
392 OrderTypeAttributes (element);
393 XmlTextWriter writer = new XmlTextWriter(output);
394 writer.Formatting = Formatting.Indented;
395 writer.Indentation = 2;
396 writer.IndentChar = ' ';
397 element.WriteTo(writer);
401 private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
403 Action<string> creator = file => {
404 using (var writer = OpenWrite (file, mode))
408 MdocFile.UpdateFile (filename, creator);
411 private static void OrderTypeAttributes (XmlElement e)
413 foreach (XmlElement type in e.SelectNodes ("//Type")) {
414 OrderTypeAttributes (type.Attributes);
418 static readonly string[] TypeAttributeOrder = {
419 "Name", "FullName", "FullNameSP", "Maintainer"
422 private static void OrderTypeAttributes (XmlAttributeCollection c)
424 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
425 for (int i = 0; i < c.Count; ++i) {
426 XmlAttribute a = c [i];
427 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
428 if (a.Name == TypeAttributeOrder [j]) {
434 for (int i = attrs.Length-1; i >= 0; --i) {
435 XmlAttribute n = attrs [i];
438 XmlAttribute r = null;
439 for (int j = i+1; j < attrs.Length; ++j) {
440 if (attrs [j] != null) {
448 c.InsertBefore (n, r);
452 private XmlDocument CreateIndexStub()
454 XmlDocument index = new XmlDocument();
456 XmlElement index_root = index.CreateElement("Overview");
457 index.AppendChild(index_root);
459 if (assemblies.Count == 0)
460 throw new Exception ("No assembly");
462 XmlElement index_assemblies = index.CreateElement("Assemblies");
463 index_root.AppendChild(index_assemblies);
465 XmlElement index_remarks = index.CreateElement("Remarks");
466 index_remarks.InnerText = "To be added.";
467 index_root.AppendChild(index_remarks);
469 XmlElement index_copyright = index.CreateElement("Copyright");
470 index_copyright.InnerText = "To be added.";
471 index_root.AppendChild(index_copyright);
473 XmlElement index_types = index.CreateElement("Types");
474 index_root.AppendChild(index_types);
479 private static void WriteNamespaceStub(string ns, string outdir) {
480 XmlDocument index = new XmlDocument();
482 XmlElement index_root = index.CreateElement("Namespace");
483 index.AppendChild(index_root);
485 index_root.SetAttribute("Name", ns);
487 XmlElement index_docs = index.CreateElement("Docs");
488 index_root.AppendChild(index_docs);
490 XmlElement index_summary = index.CreateElement("summary");
491 index_summary.InnerText = "To be added.";
492 index_docs.AppendChild(index_summary);
494 XmlElement index_remarks = index.CreateElement("remarks");
495 index_remarks.InnerText = "To be added.";
496 index_docs.AppendChild(index_remarks);
498 WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
499 writer => WriteXml (index.DocumentElement, writer));
502 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
504 var index = CreateIndexForTypes (dest);
506 var found = new HashSet<string> ();
507 foreach (AssemblyDefinition assembly in assemblies) {
508 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames)) {
509 string relpath = DoUpdateType (type, basepath, dest);
513 found.Add (type.FullName);
518 index.Add (assembly);
526 if (ignore_missing_types)
529 var notFound = from n in typenames where !found.Contains (n) select n;
531 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
534 class IndexForTypes {
540 XmlElement index_types;
541 XmlElement index_assemblies;
543 public IndexForTypes (MDocUpdater app, string indexFile, XmlDocument index)
546 this.indexFile = indexFile;
549 index_types = WriteElement (index.DocumentElement, "Types");
550 index_assemblies = WriteElement (index.DocumentElement, "Assemblies");
553 public void Add (AssemblyDefinition assembly)
555 if (index_assemblies.SelectSingleNode ("Assembly[@Name='" + assembly.Name.Name + "']") != null)
558 app.AddIndexAssembly (assembly, index_assemblies);
561 public void Add (TypeDefinition type)
563 app.AddIndexType (type, index_types);
568 SortIndexEntries (index_types);
569 WriteFile (indexFile, FileMode.Create,
570 writer => WriteXml (index.DocumentElement, writer));
574 IndexForTypes CreateIndexForTypes (string dest)
576 string indexFile = Path.Combine (dest, "index.xml");
577 if (File.Exists (indexFile))
579 return new IndexForTypes (this, indexFile, CreateIndexStub ());
582 /// <summary>Constructs the presumed path to the type's documentation file</summary>
583 /// <returns><c>true</c>, if the type file was found, <c>false</c> otherwise.</returns>
584 /// <param name="result">A typle that contains 1) the 'reltypefile', 2) the 'typefile', and 3) the file info</param>
585 bool TryFindTypeFile(string nsname, string typename, string basepath, out Tuple<string, string, FileInfo> result) {
586 string reltypefile = DocUtils.PathCombine (nsname, typename + ".xml");
587 string typefile = Path.Combine (basepath, reltypefile);
588 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
590 result = new Tuple<string, string, FileInfo> (reltypefile, typefile, file);
595 public string DoUpdateType (TypeDefinition type, string basepath, string dest)
597 if (type.Namespace == null)
598 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
600 if (!IsPublic (type))
603 // Must get the A+B form of the type name.
604 string typename = GetTypeFileName(type);
605 string nsname = DocUtils.GetNamespace (type);
607 // Find the file, if it exists
608 string[] searchLocations = new string[] {
612 if (MDocUpdater.HasDroppedNamespace (type)) {
613 // If dropping namespace, types may have moved into a couple of different places.
614 var newSearchLocations = searchLocations.Union (new string[] {
615 string.Format ("{0}.{1}", droppedNamespace, nsname),
616 nsname.Replace (droppedNamespace + ".", string.Empty),
617 MDocUpdater.droppedNamespace
620 searchLocations = newSearchLocations.ToArray ();
623 string reltypefile="", typefile="";
624 System.IO.FileInfo file = null;
626 foreach (var f in searchLocations) {
627 Tuple<string, string, FileInfo> result;
628 bool fileExists = TryFindTypeFile (f, typename, basepath, out result);
631 reltypefile = result.Item1;
632 typefile = result.Item2;
639 if (file == null || !file.Exists) {
640 // we were not able to find a file, let's use the original type informatio.
641 // so that we create the stub in the right place.
642 Tuple<string, string, FileInfo> result;
643 TryFindTypeFile (nsname, typename, basepath, out result);
645 reltypefile = result.Item1;
646 typefile = result.Item2;
650 string output = null;
653 } else if (dest == "-") {
656 output = Path.Combine (dest, reltypefile);
659 if (file != null && file.Exists) {
661 XmlDocument basefile = new XmlDocument();
663 basefile.Load(typefile);
664 } catch (Exception e) {
665 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
668 DoUpdateType2("Updating", basefile, type, output, false);
671 XmlElement td = StubType(type, output);
675 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
678 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
684 public void DoUpdateNS (string ns, string nspath, string outpath)
686 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
687 AssemblyDefinition assembly = assemblies [0];
689 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
690 XmlDocument basefile = new XmlDocument();
691 string typefile = Path.Combine(nspath, file.Name);
693 basefile.Load(typefile);
694 } catch (Exception e) {
695 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
699 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
700 TypeDefinition type = assembly.GetType(typename);
703 if (!string.IsNullOrWhiteSpace (droppedNamespace)) {
704 string nameWithNs = string.Format ("{0}.{1}", droppedNamespace, typename);
705 type = assembly.GetType (nameWithNs);
707 Warning ("Type no longer in assembly: " + typename);
714 seenTypes[type] = seenTypes;
715 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false);
718 // Stub types not in the directory
719 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
720 if (type.Namespace != ns || seenTypes.ContainsKey(type))
723 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"));
724 if (td == null) continue;
728 private static string GetTypeFileName (TypeReference type)
730 return filenameFormatter.GetName (type);
733 public static string GetTypeFileName (string typename)
735 StringBuilder filename = new StringBuilder (typename.Length);
739 for (int i = 0; i < typename.Length; ++i) {
740 char c = typename [i];
749 filename.Append ('`').Append ((numArgs+1).ToString());
764 return filename.ToString ();
767 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
769 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
770 index_assembly.SetAttribute ("Name", assembly.Name.Name);
771 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
773 AssemblyNameDefinition name = assembly.Name;
774 if (name.HasPublicKey) {
775 XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
776 var key = new StringBuilder (name.PublicKey.Length*3 + 2);
778 foreach (byte b in name.PublicKey)
779 key.AppendFormat ("{0,2:x2} ", b);
781 pubkey.InnerText = key.ToString ();
782 index_assembly.AppendChild (pubkey);
785 if (!string.IsNullOrEmpty (name.Culture)) {
786 XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
787 culture.InnerText = name.Culture;
788 index_assembly.AppendChild (culture);
791 MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
792 parent.AppendChild(index_assembly);
795 private void AddIndexType (TypeDefinition type, XmlElement index_types)
797 string typename = GetTypeFileName(type);
799 // Add namespace and type nodes into the index file as needed
800 string ns = DocUtils.GetNamespace (type);
801 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode ("Namespace[@Name='" + ns + "']");
802 if (nsnode == null) {
803 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
804 nsnode.SetAttribute ("Name", ns);
805 index_types.AppendChild (nsnode);
807 string doc_typename = GetDocTypeName (type);
808 XmlElement typenode = (XmlElement) nsnode.SelectSingleNode ("Type[@Name='" + typename + "']");
809 if (typenode == null) {
810 typenode = index_types.OwnerDocument.CreateElement ("Type");
811 typenode.SetAttribute ("Name", typename);
812 nsnode.AppendChild (typenode);
814 if (typename != doc_typename)
815 typenode.SetAttribute("DisplayName", doc_typename);
817 typenode.RemoveAttribute("DisplayName");
819 typenode.SetAttribute ("Kind", GetTypeKind (type));
822 private void DoUpdateAssemblies (string source, string dest)
824 string indexfile = dest + "/index.xml";
826 if (System.IO.File.Exists(indexfile)) {
827 index = new XmlDocument();
828 index.Load(indexfile);
831 ClearElement(index.DocumentElement, "Assembly");
832 ClearElement(index.DocumentElement, "Attributes");
834 index = CreateIndexStub();
837 string defaultTitle = "Untitled";
838 if (assemblies.Count == 1)
839 defaultTitle = assemblies[0].Name.Name;
840 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
842 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
843 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
844 index_assemblies.RemoveAll ();
847 HashSet<string> goodfiles = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
849 foreach (AssemblyDefinition assm in assemblies) {
850 AddIndexAssembly (assm, index_assemblies);
851 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
854 SortIndexEntries (index_types);
856 CleanupFiles (dest, goodfiles);
857 CleanupIndexTypes (index_types, goodfiles);
858 CleanupExtensions (index_types);
860 WriteFile (indexfile, FileMode.Create,
861 writer => WriteXml(index.DocumentElement, writer));
864 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
866 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
868 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
869 string typename = GetTypeFileName(type);
870 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0 || forwardedTypes.Contains (type.FullName))
873 string reltypepath = DoUpdateType (type, source, dest);
874 if (reltypepath == null)
877 // Add namespace and type nodes into the index file as needed
878 AddIndexType (type, index_types);
880 // Ensure the namespace index file exists
881 string namespaceToUse = type.Namespace;
882 if (HasDroppedNamespace(assembly)) {
883 namespaceToUse = string.Format ("{0}.{1}", droppedNamespace, namespaceToUse);
885 string onsdoc = DocUtils.PathCombine (dest, namespaceToUse + ".xml");
886 string nsdoc = DocUtils.PathCombine (dest, "ns-" + namespaceToUse + ".xml");
887 if (File.Exists (onsdoc)) {
888 File.Move (onsdoc, nsdoc);
891 if (!File.Exists (nsdoc)) {
892 Console.WriteLine("New Namespace File: " + type.Namespace);
893 WriteNamespaceStub(namespaceToUse, dest);
896 goodfiles.Add (reltypepath);
900 private static void SortIndexEntries (XmlElement indexTypes)
902 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
903 XmlNodeComparer c = new AttributeNameComparer ();
904 SortXmlNodes (indexTypes, namespaces, c);
906 for (int i = 0; i < namespaces.Count; ++i)
907 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
910 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
912 MyXmlNodeList l = new MyXmlNodeList (children.Count);
913 for (int i = 0; i < children.Count; ++i)
914 l.Add (children [i]);
916 for (int i = l.Count - 1; i > 0; --i) {
917 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
921 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
923 public abstract int Compare (XmlNode x, XmlNode y);
925 public int Compare (object x, object y)
927 return Compare ((XmlNode) x, (XmlNode) y);
931 class AttributeNameComparer : XmlNodeComparer {
934 public AttributeNameComparer ()
939 public AttributeNameComparer (string attribute)
941 this.attribute = attribute;
944 public override int Compare (XmlNode x, XmlNode y)
946 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
950 class VersionComparer : XmlNodeComparer {
951 public override int Compare (XmlNode x, XmlNode y)
953 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
954 string a = GetVersion (x.InnerText);
955 string b = GetVersion (y.InnerText);
956 return new Version (a).CompareTo (new Version (b));
959 static string GetVersion (string v)
961 int n = v.IndexOf ("x");
964 return v.Substring (0, n-1);
968 private static string GetTypeKind (TypeDefinition type)
971 return "Enumeration";
972 if (type.IsValueType)
974 if (type.IsInterface)
976 if (DocUtils.IsDelegate (type))
978 if (type.IsClass || type.FullName == "System.Enum") // FIXME
980 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
983 public static bool IsPublic (TypeDefinition type)
985 TypeDefinition decl = type;
986 while (decl != null) {
987 if (!(decl.IsPublic || decl.IsNestedPublic ||
988 decl.IsNestedFamily || decl.IsNestedFamily || decl.IsNestedFamilyOrAssembly)) {
991 decl = (TypeDefinition) decl.DeclaringType;
996 private void CleanupFiles (string dest, HashSet<string> goodfiles)
998 // Look for files that no longer correspond to types
999 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
1000 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
1001 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
1002 if (!goodfiles.Contains (relTypeFile)) {
1003 XmlDocument doc = new XmlDocument ();
1004 doc.Load (typefile.FullName);
1005 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
1006 string assemblyName = doc.SelectSingleNode ("/Type/AssemblyInfo/AssemblyName").InnerText;
1007 AssemblyDefinition assembly = assemblies.FirstOrDefault (a => a.Name.Name == assemblyName);
1008 if (e != null && !no_assembly_versions && assembly != null && assemblyName != null && UpdateAssemblyVersions(e, assembly, GetAssemblyVersions(assemblyName), false)) {
1009 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
1010 WriteXml(doc.DocumentElement, writer);
1011 goodfiles.Add (relTypeFile);
1015 if (string.IsNullOrWhiteSpace (PreserveTag)) { // only do this if there was no -preserve
1016 string newname = typefile.FullName + ".remove";
1017 try { System.IO.File.Delete(newname); } catch (Exception) { }
1018 try { typefile.MoveTo(newname); } catch (Exception) { }
1019 Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
1026 private static TextWriter OpenWrite (string path, FileMode mode)
1028 var w = new StreamWriter (
1029 new FileStream (path, mode),
1030 new UTF8Encoding (false)
1036 private string[] GetAssemblyVersions (string assemblyName)
1038 return (from a in assemblies
1039 where a.Name.Name == assemblyName
1040 select GetAssemblyVersion (a)).ToArray ();
1043 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
1045 // Look for type nodes that no longer correspond to types
1046 MyXmlNodeList remove = new MyXmlNodeList ();
1047 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
1048 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
1049 if (!goodfiles.Contains (fulltypename)) {
1050 remove.Add (typenode);
1053 foreach (XmlNode n in remove)
1054 n.ParentNode.RemoveChild (n);
1057 private void CleanupExtensions (XmlElement index_types)
1059 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
1060 if (extensionMethods.Count == 0) {
1063 index_types.SelectSingleNode ("/Overview").RemoveChild (e);
1067 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
1068 index_types.SelectSingleNode ("/Overview").AppendChild (e);
1072 extensionMethods.Sort (DefaultExtensionMethodComparer);
1073 foreach (XmlNode m in extensionMethods) {
1074 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
1078 class ExtensionMethodComparer : XmlNodeComparer {
1079 public override int Compare (XmlNode x, XmlNode y)
1081 XmlNode xLink = x.SelectSingleNode ("Member/Link");
1082 XmlNode yLink = y.SelectSingleNode ("Member/Link");
1084 int n = xLink.Attributes ["Type"].Value.CompareTo (
1085 yLink.Attributes ["Type"].Value);
1088 n = xLink.Attributes ["Member"].Value.CompareTo (
1089 yLink.Attributes ["Member"].Value);
1090 if (n == 0 && !object.ReferenceEquals (x, y))
1091 throw new InvalidOperationException ("Duplicate extension method found!");
1096 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
1098 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince)
1100 Console.WriteLine(message + ": " + type.FullName);
1102 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
1104 // Update type metadata
1105 UpdateType(basefile.DocumentElement, type);
1107 // Update existing members. Delete member nodes that no longer should be there,
1108 // and remember what members are already documented so we don't add them again.
1110 MyXmlNodeList todelete = new MyXmlNodeList ();
1112 foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
1113 XmlElement oldmember = info.Node;
1114 MemberReference oldmember2 = info.Member;
1115 string sig = oldmember2 != null ? memberFormatters [0].GetDeclaration (oldmember2) : null;
1117 // Interface implementations and overrides are deleted from the docs
1118 // unless the overrides option is given.
1119 if (oldmember2 != null && sig == null)
1122 // Deleted (or signature changed)
1123 if (oldmember2 == null) {
1124 if (!no_assembly_versions && UpdateAssemblyVersions (oldmember, type.Module.Assembly, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
1127 DeleteMember ("Member Removed", output, oldmember, todelete, type);
1132 if (seenmembers.ContainsKey (sig)) {
1133 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
1134 // ignore, already seen
1136 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
1137 DeleteMember ("Duplicate Member Found", output, oldmember, todelete, type);
1139 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
1143 // Update signature information
1146 // get all apistyles of sig from info.Node
1147 var styles = oldmember.GetElementsByTagName ("MemberSignature").Cast<XmlElement> ()
1148 .Where (x => x.GetAttribute ("Language") == "C#" && !seenmembers.ContainsKey(x.GetAttribute("Value")))
1149 .Select (x => x.GetAttribute ("Value"));
1151 foreach (var stylesig in styles) {
1152 seenmembers.Add (stylesig, oldmember);
1155 foreach (XmlElement oldmember in todelete)
1156 oldmember.ParentNode.RemoveChild (oldmember);
1159 if (!DocUtils.IsDelegate (type)) {
1160 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
1161 var typemembers = type.GetMembers()
1163 if (m is TypeDefinition) return false;
1164 string sig = memberFormatters [0].GetDeclaration (m);
1165 if (sig == null) return false;
1166 if (seenmembers.ContainsKey(sig)) return false;
1168 // Verify that the member isn't an explicitly implemented
1169 // member of an internal interface, in which case we shouldn't return true.
1170 MethodDefinition methdef = null;
1171 if (m is MethodDefinition)
1172 methdef = m as MethodDefinition;
1173 else if (m is PropertyDefinition) {
1174 var prop = m as PropertyDefinition;
1175 methdef = prop.GetMethod ?? prop.SetMethod;
1178 if (methdef != null) {
1179 TypeReference iface;
1180 MethodReference imethod;
1182 if (methdef.Overrides.Count == 1) {
1183 DocUtils.GetInfoForExplicitlyImplementedMethod (methdef, out iface, out imethod);
1184 if (!IsPublic (iface.Resolve ())) return false;
1191 foreach (MemberReference m in typemembers) {
1192 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
1193 if (mm == null) continue;
1195 if (MDocUpdater.SwitchingToMagicTypes) {
1196 // this is a unified style API that obviously doesn't exist in the classic API. Let's mark
1197 // it with apistyle="unified", so that it's not displayed for classic style APIs
1198 mm.SetAttribute ("apistyle", "unified");
1201 members.AppendChild( mm );
1203 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
1208 // Import code snippets from files
1209 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
1210 if (!(code is XmlElement)) continue;
1211 string file = ((XmlElement)code).GetAttribute("src");
1212 string lang = ((XmlElement)code).GetAttribute("lang");
1214 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
1216 code.InnerText = src;
1220 if (insertSince && since != null) {
1221 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
1222 docs.AppendChild (CreateSinceNode (basefile));
1226 XmlElement d = basefile.DocumentElement ["Docs"];
1227 XmlElement m = basefile.DocumentElement ["Members"];
1228 if (d != null && m != null)
1229 basefile.DocumentElement.InsertBefore (
1230 basefile.DocumentElement.RemoveChild (d), m);
1231 SortTypeMembers (m);
1235 WriteXml(basefile.DocumentElement, Console.Out);
1237 FileInfo file = new FileInfo (output);
1238 if (!file.Directory.Exists) {
1239 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
1240 file.Directory.Create ();
1242 WriteFile (output, FileMode.Create,
1243 writer => WriteXml(basefile.DocumentElement, writer));
1247 private string GetCodeSource (string lang, string file)
1250 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
1251 // Grab the specified region
1252 string region = "#region " + file.Substring (anchorStart + 4);
1253 file = file.Substring (0, anchorStart + 3);
1255 using (StreamReader reader = new StreamReader (file)) {
1257 StringBuilder src = new StringBuilder ();
1259 while ((line = reader.ReadLine ()) != null) {
1260 if (line.Trim() == region) {
1261 indent = line.IndexOf (region);
1264 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
1269 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
1272 return src.ToString ();
1274 } catch (Exception e) {
1275 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
1276 file, region, show_exceptions ? e.ToString () : e.Message);
1281 using (StreamReader reader = new StreamReader (file))
1282 return reader.ReadToEnd ();
1283 } catch (Exception e) {
1284 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
1289 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete, TypeDefinition type)
1291 string format = output != null
1292 ? "{0}: File='{1}'; Signature='{4}'"
1293 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1297 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1298 member.Attributes ["MemberName"].Value,
1299 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
1300 if (!delete && MemberDocsHaveUserContent (member)) {
1301 Warning ("Member deletions must be enabled with the --delete option.");
1302 } else if (HasDroppedNamespace (type)) {
1303 // if we're dropping the namespace, add the "classic style"
1304 var existingAttribute = member.Attributes ["apistyle"];
1305 if (existingAttribute != null) {
1306 existingAttribute.Value = "classic";
1308 // add the attribute and do not remove
1309 XmlAttribute apistyleAttr = member.OwnerDocument.CreateAttribute ("apistyle");
1311 apistyleAttr.Value = "classic";
1313 member.Attributes.Append (apistyleAttr);
1315 } else if (!HasDroppedNamespace (type) && member.Attributes ["apistyle"] != null && member.Attributes ["apistyle"].Value == "unified") {
1316 // do nothing if there's an apistyle=new attribute and we haven't dropped the namespace
1317 } else if (!string.IsNullOrWhiteSpace (PreserveTag)) {
1320 todelete.Add (member);
1325 class MemberComparer : XmlNodeComparer {
1326 public override int Compare (XmlNode x, XmlNode y)
1329 string xMemberName = x.Attributes ["MemberName"].Value;
1330 string yMemberName = y.Attributes ["MemberName"].Value;
1332 // generic methods *end* with '>'
1333 // it's possible for explicitly implemented generic interfaces to
1334 // contain <...> without being a generic method
1335 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1336 (r = xMemberName.CompareTo (yMemberName)) != 0)
1340 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1341 xMemberName = xMemberName.Substring (0, lt);
1342 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1343 yMemberName = yMemberName.Substring (0, lt);
1344 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1347 // if @MemberName matches, then it's either two different types of
1348 // members sharing the same name, e.g. field & property, or it's an
1349 // overloaded method.
1350 // for different type, sort based on MemberType value.
1351 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1352 y.SelectSingleNode ("MemberType").InnerText);
1356 // same type -- must be an overloaded method. Sort based on type
1357 // parameter count, then parameter count, then by the parameter
1359 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1360 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1361 if (xTypeParams.Count != yTypeParams.Count)
1362 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1363 for (int i = 0; i < xTypeParams.Count; ++i) {
1364 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1365 yTypeParams [i].Attributes ["Name"].Value);
1370 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1371 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1372 if (xParams.Count != yParams.Count)
1373 return xParams.Count <= yParams.Count ? -1 : 1;
1374 for (int i = 0; i < xParams.Count; ++i) {
1375 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1376 yParams [i].Attributes ["Type"].Value);
1380 // all parameters match, but return value might not match if it was
1381 // changed between one version and another.
1382 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1383 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1384 if (xReturn != null && yReturn != null) {
1385 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1394 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1396 private static void SortTypeMembers (XmlNode members)
1398 if (members == null)
1400 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1403 private static bool MemberDocsHaveUserContent (XmlNode e)
1405 e = (XmlElement)e.SelectSingleNode("Docs");
1406 if (e == null) return false;
1407 foreach (XmlElement d in e.SelectNodes("*"))
1408 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1413 // UPDATE HELPER FUNCTIONS
1415 // CREATE A STUB DOCUMENTATION FILE
1417 public XmlElement StubType (TypeDefinition type, string output)
1419 string typesig = typeFormatters [0].GetDeclaration (type);
1420 if (typesig == null) return null; // not publicly visible
1422 XmlDocument doc = new XmlDocument();
1423 XmlElement root = doc.CreateElement("Type");
1424 doc.AppendChild (root);
1426 DoUpdateType2 ("New Type", doc, type, output, true);
1431 private XmlElement CreateSinceNode (XmlDocument doc)
1433 XmlElement s = doc.CreateElement ("since");
1434 s.SetAttribute ("version", since);
1438 // STUBBING/UPDATING FUNCTIONS
1440 public void UpdateType (XmlElement root, TypeDefinition type)
1442 root.SetAttribute("Name", GetDocTypeName (type));
1443 root.SetAttribute("FullName", GetDocTypeFullName (type));
1445 foreach (MemberFormatter f in typeFormatters) {
1446 string element = "TypeSignature[@Language='" + f.Language + "']";
1447 string valueToUse = f.GetDeclaration (type);
1450 root.SelectNodes (element).Cast<XmlElement> ().ToArray (),
1451 x => x.GetAttribute ("Value") == valueToUse,
1452 x => x.SetAttribute ("Value", valueToUse),
1454 var node = WriteElementAttribute (root, element, "Language", f.Language, forceNewElement: true);
1455 var newnode = WriteElementAttribute (root, node, "Value", valueToUse);
1461 string assemblyInfoNodeFilter = MDocUpdater.HasDroppedNamespace (type) ? "[@apistyle='unified']" : "[not(@apistyle) or @apistyle='classic']";
1464 root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ().ToArray (),
1465 x => x.SelectSingleNode("AssemblyName").InnerText == type.Module.Assembly.Name.Name,
1466 x => WriteElementText(x, "AssemblyName", type.Module.Assembly.Name.Name),
1468 XmlElement ass = WriteElement(root, "AssemblyInfo", forceNewElement:true);
1470 if (MDocUpdater.HasDroppedNamespace (type)) ass.SetAttribute ("apistyle", "unified");
1478 foreach(var ass in root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ())
1480 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1481 if (!no_assembly_versions) {
1482 UpdateAssemblyVersions (root, type, true);
1485 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1486 foreach (var version in versions)
1487 ass.RemoveChild (version);
1489 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1490 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1492 ClearElement(ass, "AssemblyCulture");
1495 // Why-oh-why do we put assembly attributes in each type file?
1496 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1497 // since they're outdated in current docs, and a waste of space.
1498 //MakeAttributes(ass, type.Assembly, true);
1499 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1500 if (assattrs != null)
1501 ass.RemoveChild(assattrs);
1503 NormalizeWhitespace(ass);
1506 if (type.IsGenericType ()) {
1507 MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
1509 ClearElement(root, "TypeParameters");
1512 if (type.BaseType != null) {
1513 XmlElement basenode = WriteElement(root, "Base");
1515 string basetypename = GetDocTypeFullName (type.BaseType);
1516 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1517 WriteElementText(root, "Base/BaseTypeName", basetypename);
1519 // Document how this type instantiates the generic parameters of its base type
1520 TypeReference origBase = type.BaseType.GetElementType ();
1521 if (origBase.IsGenericType ()) {
1522 ClearElement(basenode, "BaseTypeArguments");
1523 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1524 IList<TypeReference> baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1525 IList<GenericParameter> baseGenParams = origBase.GenericParameters;
1526 if (baseGenArgs.Count != baseGenParams.Count)
1527 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1528 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1529 GenericParameter param = baseGenParams [i];
1530 TypeReference value = baseGenArgs [i];
1532 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1533 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1534 bta.AppendChild(arg);
1535 arg.SetAttribute ("TypeParamName", param.Name);
1536 arg.InnerText = GetDocTypeFullName (value);
1540 ClearElement(root, "Base");
1543 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1544 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1545 List<string> interface_names = userInterfaces
1546 .Select (iface => GetDocTypeFullName (iface))
1550 XmlElement interfaces = WriteElement(root, "Interfaces");
1551 interfaces.RemoveAll();
1552 foreach (string iname in interface_names) {
1553 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1554 interfaces.AppendChild(iface);
1555 WriteElementText(iface, "InterfaceName", iname);
1558 ClearElement(root, "Interfaces");
1561 MakeAttributes (root, GetCustomAttributes (type), type);
1563 if (DocUtils.IsDelegate (type)) {
1564 MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
1565 var member = type.GetMethod ("Invoke");
1566 MakeParameters(root, member, member.Parameters);
1567 MakeReturnValue(root, member);
1570 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1571 MakeDocNode (typeInfo);
1573 if (!DocUtils.IsDelegate (type))
1574 WriteElement (root, "Members");
1576 OrderTypeNodes (root, root.ChildNodes);
1577 NormalizeWhitespace(root);
1580 static readonly string[] TypeNodeOrder = {
1584 "ThreadingSafetyStatement",
1585 "ThreadSafetyStatement",
1597 static void OrderTypeNodes (XmlNode member, XmlNodeList children)
1599 ReorderNodes (member, children, TypeNodeOrder);
1602 internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1604 List<T> l = new List<T> (list);
1609 private void UpdateMember (DocsNodeInfo info)
1611 XmlElement me = (XmlElement) info.Node;
1612 MemberReference mi = info.Member;
1614 foreach (MemberFormatter f in memberFormatters) {
1615 string element = "MemberSignature[@Language='" + f.Language + "']";
1617 var valueToUse = f.GetDeclaration (mi);
1620 me.SelectNodes (element).Cast<XmlElement> ().ToArray(),
1621 x => x.GetAttribute("Value") == valueToUse,
1622 x => x.SetAttribute ("Value", valueToUse),
1624 var node = WriteElementAttribute (me, element, "Language", f.Language, forceNewElement:true);
1625 var newNode = WriteElementAttribute (me, node, "Value", valueToUse);
1632 WriteElementText(me, "MemberType", GetMemberType(mi));
1634 if (!no_assembly_versions) {
1635 UpdateAssemblyVersions (me, mi, true);
1638 ClearElement (me, "AssemblyInfo");
1641 MakeAttributes (me, GetCustomAttributes (mi), mi.DeclaringType);
1643 MakeReturnValue(me, mi, MDocUpdater.HasDroppedNamespace(mi));
1644 if (mi is MethodReference) {
1645 MethodReference mb = (MethodReference) mi;
1646 if (mb.IsGenericMethod ())
1647 MakeTypeParameters (me, mb.GenericParameters, mi, MDocUpdater.HasDroppedNamespace(mi));
1649 MakeParameters(me, mi, MDocUpdater.HasDroppedNamespace(mi));
1652 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1653 WriteElementText(me, "MemberValue", fieldValue);
1655 info.Node = WriteElement (me, "Docs");
1657 OrderMemberNodes (me, me.ChildNodes);
1658 UpdateExtensionMethods (me, info);
1661 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, MemberReference member) {
1662 AddXmlNode (relevant, valueMatches, setValue, makeNewNode, member.Module);
1665 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, TypeDefinition type) {
1666 AddXmlNode (relevant, valueMatches, setValue, makeNewNode, type.Module);
1669 /// <summary>Adds an xml node, reusing the node if it's available</summary>
1670 /// <param name="relevant">The existing set of nodes</param>
1671 /// <param name="valueMatches">Checks to see if the node's value matches what you're trying to write.</param>
1672 /// <param name="setValue">Sets the node's value</param>
1673 /// <param name="makeNewNode">Creates a new node, if valueMatches returns false.</param>
1674 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, ModuleDefinition module)
1676 bool shouldDuplicate = MDocUpdater.HasDroppedNamespace (module);
1677 var styleToUse = shouldDuplicate ? ApiStyle.Unified : ApiStyle.Classic;
1678 var existing = relevant;
1680 bool addedOldApiStyle = false;
1682 if (shouldDuplicate) {
1683 existing = existing.Where (n => n.HasApiStyle (styleToUse)).ToArray ();
1684 foreach (var n in relevant.Where (n => n.DoesNotHaveApiStyle (styleToUse))) {
1685 if (valueMatches (n)) {
1689 n.AddApiStyle (ApiStyle.Classic);
1690 addedOldApiStyle = true;
1695 if (!existing.Any ()) {
1696 var newNode = makeNewNode ();
1697 if (shouldDuplicate && addedOldApiStyle) {
1698 newNode.AddApiStyle (ApiStyle.Unified);
1702 var itemToReuse = existing.First ();
1703 setValue (itemToReuse);
1705 if (shouldDuplicate && addedOldApiStyle) {
1706 itemToReuse.AddApiStyle (styleToUse);
1712 static readonly string[] MemberNodeOrder = {
1727 static void OrderMemberNodes (XmlNode member, XmlNodeList children)
1729 ReorderNodes (member, children, MemberNodeOrder);
1732 static void ReorderNodes (XmlNode node, XmlNodeList children, string[] ordering)
1734 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1735 for (int i = 0; i < ordering.Length; ++i) {
1736 for (int j = 0; j < children.Count; ++j) {
1737 XmlNode c = children [j];
1738 if (c.Name == ordering [i]) {
1739 newChildren.Add (c);
1743 if (newChildren.Count >= 0)
1744 node.PrependChild ((XmlNode) newChildren [0]);
1745 for (int i = 1; i < newChildren.Count; ++i) {
1746 XmlNode prev = (XmlNode) newChildren [i-1];
1747 XmlNode cur = (XmlNode) newChildren [i];
1748 node.RemoveChild (cur);
1749 node.InsertAfter (cur, prev);
1753 IEnumerable<string> GetCustomAttributes (MemberReference mi)
1755 IEnumerable<string> attrs = Enumerable.Empty<string>();
1757 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1759 attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1761 PropertyDefinition pd = mi as PropertyDefinition;
1763 if (pd.GetMethod != null)
1764 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1765 if (pd.SetMethod != null)
1766 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1769 EventDefinition ed = mi as EventDefinition;
1771 if (ed.AddMethod != null)
1772 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1773 if (ed.RemoveMethod != null)
1774 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1780 IEnumerable<string> GetCustomAttributes (IList<CustomAttribute> attributes, string prefix)
1782 foreach (CustomAttribute attribute in attributes.OrderBy (ca => ca.AttributeType.FullName)) {
1784 TypeDefinition attrType = attribute.AttributeType as TypeDefinition;
1785 if (attrType != null && !IsPublic (attrType))
1787 if (slashdocFormatter.GetName (attribute.AttributeType) == null)
1790 if (Array.IndexOf (IgnorableAttributes, attribute.AttributeType.FullName) >= 0)
1793 StringList fields = new StringList ();
1795 for (int i = 0; i < attribute.ConstructorArguments.Count; ++i) {
1796 CustomAttributeArgument argument = attribute.ConstructorArguments [i];
1797 fields.Add (MakeAttributesValueString (
1802 (from namedArg in attribute.Fields
1803 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value })
1805 (from namedArg in attribute.Properties
1806 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value }))
1807 .OrderBy (v => v.Name);
1808 foreach (var d in namedArgs)
1809 fields.Add (string.Format ("{0}={1}", d.Name,
1810 MakeAttributesValueString (d.Value, d.Type)));
1812 string a2 = String.Join(", ", fields.ToArray ());
1813 if (a2 != "") a2 = "(" + a2 + ")";
1815 string name = attribute.GetDeclaringType();
1816 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1817 yield return prefix + name + a2;
1821 static readonly string[] ValidExtensionMembers = {
1830 static readonly string[] ValidExtensionDocMembers = {
1836 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1838 MethodDefinition me = info.Member as MethodDefinition;
1841 if (info.Parameters.Count < 1)
1843 if (!DocUtils.IsExtensionMethod (me))
1846 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1847 XmlNode member = e.CloneNode (true);
1848 em.AppendChild (member);
1849 RemoveExcept (member, ValidExtensionMembers);
1850 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1851 WriteElementText (member, "MemberType", "ExtensionMethod");
1852 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1853 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1854 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1855 member.AppendChild (link);
1856 AddTargets (em, info);
1858 extensionMethods.Add (em);
1861 private static void RemoveExcept (XmlNode node, string[] except)
1865 MyXmlNodeList remove = null;
1866 foreach (XmlNode n in node.ChildNodes) {
1867 if (Array.BinarySearch (except, n.Name) < 0) {
1869 remove = new MyXmlNodeList ();
1874 foreach (XmlNode n in remove)
1875 node.RemoveChild (n);
1878 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1880 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1881 member.PrependChild (targets);
1882 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1883 AppendElementAttributeText (targets, "Target", "Type",
1884 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1887 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1888 IList<TypeReference> constraints = gp.Constraints;
1889 if (constraints.Count == 0)
1890 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1892 foreach (TypeReference c in constraints)
1893 AppendElementAttributeText(targets, "Target", "Type",
1894 slashdocFormatter.GetDeclaration (c));
1898 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1901 TypeDefinition type = field.DeclaringType.Resolve ();
1902 if (type != null && type.IsEnum) return false;
1904 if (type != null && type.IsGenericType ()) return false;
1905 if (!field.HasConstant)
1907 if (field.IsLiteral) {
1908 object val = field.Constant;
1909 if (val == null) value = "null";
1910 else if (val is Enum) value = val.ToString();
1911 else if (val is IFormattable) {
1912 value = ((IFormattable)val).ToString();
1914 value = "\"" + value + "\"";
1916 if (value != null && value != "")
1922 // XML HELPER FUNCTIONS
1924 internal static XmlElement WriteElement(XmlNode parent, string element, bool forceNewElement = false) {
1925 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1926 if (ret == null || forceNewElement) {
1927 string[] path = element.Split('/');
1928 foreach (string p in path) {
1929 ret = (XmlElement)parent.SelectSingleNode(p);
1930 if (ret == null || forceNewElement) {
1932 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1933 ename = ename.Substring(0, ename.IndexOf('['));
1934 ret = parent.OwnerDocument.CreateElement(ename);
1935 parent.AppendChild(ret);
1944 private static XmlElement WriteElementText(XmlNode parent, string element, string value, bool forceNewElement = false) {
1945 XmlElement node = WriteElement(parent, element, forceNewElement: forceNewElement);
1946 node.InnerText = value;
1950 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1952 XmlElement n = parent.OwnerDocument.CreateElement (element);
1953 parent.AppendChild (n);
1954 n.InnerText = value;
1958 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1960 XmlElement n = parent.OwnerDocument.CreateElement (element);
1961 parent.AppendChild (n);
1962 n.SetAttribute (attribute, value);
1966 internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
1968 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1969 dest.AppendChild (copy);
1973 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1974 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1977 node = WriteElement(parent, element);
1978 node.InnerText = value;
1980 private static XmlElement WriteElementAttribute(XmlElement parent, string element, string attribute, string value, bool forceNewElement = false) {
1981 XmlElement node = WriteElement(parent, element, forceNewElement:forceNewElement);
1982 return WriteElementAttribute (parent, node, attribute, value);
1984 private static XmlElement WriteElementAttribute(XmlElement parent, XmlElement node, string attribute, string value) {
1985 if (node.GetAttribute (attribute) != value) {
1986 node.SetAttribute (attribute, value);
1990 internal static void ClearElement(XmlElement parent, string name) {
1991 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1993 parent.RemoveChild(node);
1996 // DOCUMENTATION HELPER FUNCTIONS
1998 private void MakeDocNode (DocsNodeInfo info)
2000 List<GenericParameter> genericParams = info.GenericParameters;
2001 IList<ParameterDefinition> parameters = info.Parameters;
2002 TypeReference returntype = info.ReturnType;
2003 bool returnisreturn = info.ReturnIsReturn;
2004 XmlElement e = info.Node;
2005 bool addremarks = info.AddRemarks;
2007 WriteElementInitialText(e, "summary", "To be added.");
2009 if (parameters != null) {
2010 string[] values = new string [parameters.Count];
2011 for (int i = 0; i < values.Length; ++i)
2012 values [i] = parameters [i].Name;
2013 UpdateParameters (e, "param", values);
2016 if (genericParams != null) {
2017 string[] values = new string [genericParams.Count];
2018 for (int i = 0; i < values.Length; ++i)
2019 values [i] = genericParams [i].Name;
2020 UpdateParameters (e, "typeparam", values);
2023 string retnodename = null;
2024 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
2025 retnodename = returnisreturn ? "returns" : "value";
2026 string retnodename_other = !returnisreturn ? "returns" : "value";
2028 // If it has a returns node instead of a value node, change its name.
2029 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
2030 if (retother != null) {
2031 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
2032 foreach (XmlNode node in retother)
2033 retnode.AppendChild(node.CloneNode(true));
2034 e.ReplaceChild(retnode, retother);
2036 WriteElementInitialText(e, retnodename, "To be added.");
2039 ClearElement(e, "returns");
2040 ClearElement(e, "value");
2044 WriteElementInitialText(e, "remarks", "To be added.");
2046 if (exceptions.HasValue && info.Member != null &&
2047 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
2048 UpdateExceptions (e, info.Member);
2051 foreach (DocumentationImporter importer in importers)
2052 importer.ImportDocumentation (info);
2054 OrderDocsNodes (e, e.ChildNodes);
2055 NormalizeWhitespace(e);
2058 static readonly string[] DocsNodeOrder = {
2059 "typeparam", "param", "summary", "returns", "value", "remarks",
2062 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
2064 ReorderNodes (docs, children, DocsNodeOrder);
2068 private void UpdateParameters (XmlElement e, string element, string[] values)
2070 if (values != null) {
2071 XmlNode[] paramnodes = new XmlNode[values.Length];
2073 // Some documentation had param nodes with leading spaces.
2074 foreach (XmlElement paramnode in e.SelectNodes(element)){
2075 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
2078 // If a member has only one parameter, we can track changes to
2079 // the name of the parameter easily.
2080 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
2081 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
2084 bool reinsert = false;
2086 // Pick out existing and still-valid param nodes, and
2087 // create nodes for parameters not in the file.
2088 Hashtable seenParams = new Hashtable();
2089 for (int pi = 0; pi < values.Length; pi++) {
2090 string p = values [pi];
2093 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
2094 if (paramnodes[pi] != null) continue;
2096 XmlElement pe = e.OwnerDocument.CreateElement(element);
2097 pe.SetAttribute("name", p);
2098 pe.InnerText = "To be added.";
2099 paramnodes[pi] = pe;
2103 // Remove parameters that no longer exist and check all params are in the right order.
2105 MyXmlNodeList todelete = new MyXmlNodeList ();
2106 foreach (XmlElement paramnode in e.SelectNodes(element)) {
2107 string name = paramnode.GetAttribute("name");
2108 if (!seenParams.ContainsKey(name)) {
2109 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2110 Warning ("The following param node can only be deleted if the --delete option is given: ");
2111 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
2113 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
2114 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2118 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
2119 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2120 e.ParentNode.Attributes ["MemberName"].Value,
2123 Warning ("\tValue={0}", paramnode.OuterXml);
2125 todelete.Add (paramnode);
2130 if ((int)seenParams[name] != idx)
2136 foreach (XmlNode n in todelete) {
2137 n.ParentNode.RemoveChild (n);
2140 // Re-insert the parameter nodes at the top of the doc section.
2142 for (int pi = values.Length-1; pi >= 0; pi--)
2143 e.PrependChild(paramnodes[pi]);
2145 // Clear all existing param nodes
2146 foreach (XmlNode paramnode in e.SelectNodes(element)) {
2147 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2148 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
2149 Console.WriteLine(paramnode.OuterXml);
2151 paramnode.ParentNode.RemoveChild(paramnode);
2157 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
2159 string existingName = pe.GetAttribute ("name");
2160 pe.SetAttribute ("name", newName);
2161 if (existingName == newName)
2163 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
2164 if (paramref.GetAttribute ("name").Trim () == existingName)
2165 paramref.SetAttribute ("name", newName);
2168 class CrefComparer : XmlNodeComparer {
2170 public CrefComparer ()
2174 public override int Compare (XmlNode x, XmlNode y)
2176 string xType = x.Attributes ["cref"].Value;
2177 string yType = y.Attributes ["cref"].Value;
2178 string xNamespace = GetNamespace (xType);
2179 string yNamespace = GetNamespace (yType);
2181 int c = xNamespace.CompareTo (yNamespace);
2184 return xType.CompareTo (yType);
2187 static string GetNamespace (string type)
2189 int n = type.LastIndexOf ('.');
2191 return type.Substring (0, n);
2192 return string.Empty;
2196 private void UpdateExceptions (XmlNode docs, MemberReference member)
2198 string indent = new string (' ', 10);
2199 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
2200 string cref = slashdocFormatter.GetDeclaration (source.Exception);
2201 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
2204 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
2205 e.SetAttribute ("cref", cref);
2206 e.InnerXml = "To be added; from:\n" + indent + "<see cref=\"" +
2207 string.Join ("\" />,\n" + indent + "<see cref=\"",
2208 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
2209 .OrderBy (s => s)) +
2211 docs.AppendChild (e);
2213 SortXmlNodes (docs, docs.SelectNodes ("exception"),
2214 new CrefComparer ());
2217 private static void NormalizeWhitespace(XmlElement e) {
2218 // Remove all text and whitespace nodes from the element so it
2219 // is outputted with nice indentation and no blank lines.
2220 ArrayList deleteNodes = new ArrayList();
2221 foreach (XmlNode n in e)
2222 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2224 foreach (XmlNode n in deleteNodes)
2225 n.ParentNode.RemoveChild(n);
2228 private static bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
2230 TypeDefinition type = member as TypeDefinition;
2232 type = member.DeclaringType as TypeDefinition;
2233 return UpdateAssemblyVersions(root, type.Module.Assembly, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
2236 private static string GetAssemblyVersion (AssemblyDefinition assembly)
2238 return assembly.Name.Version.ToString();
2241 private static bool UpdateAssemblyVersions(XmlElement root, AssemblyDefinition assembly, string[] assemblyVersions, bool add)
2243 XmlElement av = (XmlElement) root.SelectSingleNode ("AssemblyVersions");
2245 // AssemblyVersions is not part of the spec
2246 root.RemoveChild (av);
2249 string oldNodeFilter = "AssemblyInfo[not(@apistyle) or @apistyle='classic']";
2250 string newNodeFilter = "AssemblyInfo[@apistyle='unified']";
2251 string thisNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? newNodeFilter : oldNodeFilter;
2252 string thatNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? oldNodeFilter : newNodeFilter;
2254 XmlElement e = (XmlElement) root.SelectSingleNode (thisNodeFilter);
2256 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2258 if (MDocUpdater.HasDroppedNamespace (assembly)) {
2259 e.SetAttribute ("apistyle", "unified");
2262 root.AppendChild(e);
2265 var thatNode = (XmlElement) root.SelectSingleNode (thatNodeFilter);
2266 if (MDocUpdater.HasDroppedNamespace (assembly) && thatNode != null) {
2267 // there's a classic node, we should add apistyles
2268 e.SetAttribute ("apistyle", "unified");
2269 thatNode.SetAttribute ("apistyle", "classic");
2272 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
2273 .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
2275 // matches.Count > 0 && add: ignore -- already present
2276 if (matches.Count > 0 && !add) {
2277 foreach (XmlNode c in matches)
2280 else if (matches.Count == 0 && add) {
2281 foreach (string sv in assemblyVersions) {
2282 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2288 // matches.Count == 0 && !add: ignore -- already not present
2289 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2290 SortXmlNodes (e, avs, new VersionComparer ());
2292 bool anyNodesLeft = avs.Count != 0;
2293 if (!anyNodesLeft) {
2294 e.ParentNode.RemoveChild (e);
2296 return anyNodesLeft;
2299 // FIXME: get TypeReferences instead of string comparison?
2300 private static string[] IgnorableAttributes = {
2301 // Security related attributes
2302 "System.Reflection.AssemblyKeyFileAttribute",
2303 "System.Reflection.AssemblyDelaySignAttribute",
2304 // Present in @RefType
2305 "System.Runtime.InteropServices.OutAttribute",
2306 // For naming the indexer to use when not using indexers
2307 "System.Reflection.DefaultMemberAttribute",
2308 // for decimal constants
2309 "System.Runtime.CompilerServices.DecimalConstantAttribute",
2310 // compiler generated code
2311 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
2312 // more compiler generated code, e.g. iterator methods
2313 "System.Diagnostics.DebuggerHiddenAttribute",
2314 "System.Runtime.CompilerServices.FixedBufferAttribute",
2315 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
2316 // extension methods
2317 "System.Runtime.CompilerServices.ExtensionAttribute",
2318 // Used to differentiate 'object' from C#4 'dynamic'
2319 "System.Runtime.CompilerServices.DynamicAttribute",
2322 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes, TypeReference t=null)
2324 if (!attributes.Any ()) {
2325 ClearElement (root, "Attributes");
2329 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2333 e = root.OwnerDocument.CreateElement("Attributes");
2335 foreach (string attribute in attributes) {
2336 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2339 WriteElementText(ae, "AttributeName", attribute);
2342 if (e.ParentNode == null)
2343 root.AppendChild(e);
2345 NormalizeWhitespace(e);
2348 public static string MakeAttributesValueString (object v, TypeReference valueType)
2350 var formatters = new [] {
2351 new AttributeValueFormatter (),
2352 new ApplePlatformEnumFormatter (),
2353 new StandardFlagsEnumFormatter (),
2354 new DefaultAttributeValueFormatter (),
2357 ResolvedTypeInfo type = new ResolvedTypeInfo (valueType);
2358 foreach (var formatter in formatters) {
2359 string formattedValue;
2360 if (formatter.TryFormatValue (v, type, out formattedValue)) {
2361 return formattedValue;
2365 // this should never occur because the DefaultAttributeValueFormatter will always
2366 // successfully format the value ... but this is needed to satisfy the compiler :)
2367 throw new InvalidDataException (string.Format ("Unable to format attribute value ({0})", v.ToString ()));
2370 internal static IDictionary<long, string> GetEnumerationValues (TypeDefinition type)
2372 var values = new Dictionary<long, string> ();
2374 (from f in type.Fields
2375 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
2377 values [ToInt64 (f.Constant)] = f.Name;
2382 internal static long ToInt64 (object value)
2385 return (long) (ulong) value;
2386 return Convert.ToInt64 (value);
2389 private void MakeParameters (XmlElement root, MemberReference member, IList<ParameterDefinition> parameters, bool shouldDuplicateWithNew=false)
2391 XmlElement e = WriteElement(root, "Parameters");
2394 foreach (ParameterDefinition p in parameters) {
2398 var ptype = GetDocParameterType (p.ParameterType);
2399 var newPType = ptype;
2401 if (MDocUpdater.SwitchingToMagicTypes) {
2402 newPType = NativeTypeManager.ConvertFromNativeType (ptype);
2405 // now find the existing node, if it's there so we can reuse it.
2406 var nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2407 .Cast<XmlElement> ().Where (x => x.GetAttribute ("Name") == p.Name)
2410 if (nodes.Count () == 0) {
2411 // wasn't found, let's make sure it wasn't just cause the param name was changed
2412 nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2413 .Cast<XmlElement> ()
2414 .Skip (i) // this makes sure we don't inadvertently "reuse" nodes when adding new ones
2415 .Where (x => x.GetAttribute ("Name") != p.Name && (x.GetAttribute ("Type") == ptype || x.GetAttribute ("Type") == newPType))
2416 .Take(1) // there might be more than one that meets this parameter ... only take the first.
2421 x => x.GetAttribute ("Type") == ptype,
2422 x => x.SetAttribute ("Type", ptype),
2424 pe = root.OwnerDocument.CreateElement ("Parameter");
2427 pe.SetAttribute ("Name", p.Name);
2428 pe.SetAttribute ("Type", ptype);
2429 if (p.ParameterType is ByReferenceType) {
2431 pe.SetAttribute ("RefType", "out");
2433 pe.SetAttribute ("RefType", "ref");
2436 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
2445 private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams, MemberReference member, bool shouldDuplicateWithNew)
2447 if (typeParams == null || typeParams.Count == 0) {
2448 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2450 root.RemoveChild (f);
2453 XmlElement e = WriteElement(root, "TypeParameters");
2455 var nodes = e.SelectNodes ("TypeParameter").Cast<XmlElement> ().ToArray ();
2457 foreach (GenericParameter t in typeParams) {
2459 IList<TypeReference> constraints = t.Constraints;
2460 GenericParameterAttributes attrs = t.Attributes;
2466 var baseType = e.SelectSingleNode("BaseTypeName");
2467 // TODO: should this comparison take into account BaseTypeName?
2468 return x.GetAttribute("Name") == t.Name;
2470 x => {}, // no additional action required
2473 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2475 pe.SetAttribute("Name", t.Name);
2476 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""), t.DeclaringType);
2477 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2478 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2486 ce = root.OwnerDocument.CreateElement ("Constraints");
2488 pe.AppendChild (ce);
2489 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2490 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2491 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2492 AppendElementText (ce, "ParameterAttribute", "Covariant");
2493 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2494 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2495 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2496 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2497 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2498 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2499 foreach (TypeReference c in constraints) {
2500 TypeDefinition cd = c.Resolve ();
2501 AppendElementText (ce,
2502 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2503 GetDocTypeFullName (c));
2512 private void MakeParameters (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew)
2514 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2515 MakeParameters (root, mi, ((MethodDefinition)mi).Parameters, shouldDuplicateWithNew);
2516 else if (mi is MethodDefinition) {
2517 MethodDefinition mb = (MethodDefinition) mi;
2518 IList<ParameterDefinition> parameters = mb.Parameters;
2519 MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
2520 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2521 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2522 p.SetAttribute ("RefType", "this");
2525 else if (mi is PropertyDefinition) {
2526 IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
2527 if (parameters.Count > 0)
2528 MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
2532 else if (mi is FieldDefinition) return;
2533 else if (mi is EventDefinition) return;
2534 else throw new ArgumentException();
2537 internal static string GetDocParameterType (TypeReference type)
2539 return GetDocTypeFullName (type).Replace ("@", "&");
2542 private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes, bool shouldDuplicateWithNew=false)
2544 XmlElement e = WriteElement(root, "ReturnValue");
2545 var valueToUse = GetDocTypeFullName (type);
2547 AddXmlNode (e.SelectNodes("ReturnType").Cast<XmlElement> ().ToArray (),
2548 x => x.InnerText == valueToUse,
2549 x => x.InnerText = valueToUse,
2551 var newNode = WriteElementText(e, "ReturnType", valueToUse, forceNewElement: true);
2552 if (attributes != null)
2553 MakeAttributes(e, GetCustomAttributes (attributes, ""), type);
2560 private void MakeReturnValue (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew=false)
2562 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2564 else if (mi is MethodDefinition)
2565 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes, shouldDuplicateWithNew);
2566 else if (mi is PropertyDefinition)
2567 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null, shouldDuplicateWithNew);
2568 else if (mi is FieldDefinition)
2569 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null, shouldDuplicateWithNew);
2570 else if (mi is EventDefinition)
2571 MakeReturnValue (root, ((EventDefinition)mi).EventType, null, shouldDuplicateWithNew);
2573 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2576 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2578 MemberReference mi = info.Member;
2579 if (mi is TypeDefinition) return null;
2581 string sigs = memberFormatters [0].GetDeclaration (mi);
2582 if (sigs == null) return null; // not publicly visible
2584 // no documentation for property/event accessors. Is there a better way of doing this?
2585 if (mi.Name.StartsWith("get_")) return null;
2586 if (mi.Name.StartsWith("set_")) return null;
2587 if (mi.Name.StartsWith("add_")) return null;
2588 if (mi.Name.StartsWith("remove_")) return null;
2589 if (mi.Name.StartsWith("raise_")) return null;
2591 XmlElement me = doc.CreateElement("Member");
2592 me.SetAttribute("MemberName", GetMemberName (mi));
2596 if (exceptions.HasValue &&
2597 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2598 UpdateExceptions (info.Node, info.Member);
2600 if (since != null) {
2601 XmlNode docs = me.SelectSingleNode("Docs");
2602 docs.AppendChild (CreateSinceNode (doc));
2608 internal static string GetMemberName (MemberReference mi)
2610 MethodDefinition mb = mi as MethodDefinition;
2612 PropertyDefinition pi = mi as PropertyDefinition;
2615 return DocUtils.GetPropertyName (pi);
2617 StringBuilder sb = new StringBuilder (mi.Name.Length);
2618 if (!DocUtils.IsExplicitlyImplemented (mb))
2619 sb.Append (mi.Name);
2621 TypeReference iface;
2622 MethodReference ifaceMethod;
2623 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2624 sb.Append (GetDocTypeFullName (iface));
2626 sb.Append (ifaceMethod.Name);
2628 if (mb.IsGenericMethod ()) {
2629 IList<GenericParameter> typeParams = mb.GenericParameters;
2630 if (typeParams.Count > 0) {
2632 sb.Append (typeParams [0].Name);
2633 for (int i = 1; i < typeParams.Count; ++i)
2634 sb.Append (",").Append (typeParams [i].Name);
2638 return sb.ToString ();
2641 /// SIGNATURE GENERATION FUNCTIONS
2642 internal static bool IsPrivate (MemberReference mi)
2644 return memberFormatters [0].GetDeclaration (mi) == null;
2647 internal static string GetMemberType (MemberReference mi)
2649 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2650 return "Constructor";
2651 if (mi is MethodDefinition)
2653 if (mi is PropertyDefinition)
2655 if (mi is FieldDefinition)
2657 if (mi is EventDefinition)
2659 throw new ArgumentException();
2662 private static string GetDocTypeName (TypeReference type)
2664 return docTypeFormatter.GetName (type);
2667 internal static string GetDocTypeFullName (TypeReference type)
2669 return DocTypeFullMemberFormatter.Default.GetName (type);
2672 internal static string GetXPathForMember (DocumentationMember member)
2674 StringBuilder xpath = new StringBuilder ();
2675 xpath.Append ("//Members/Member[@MemberName=\"")
2676 .Append (member.MemberName)
2678 if (member.Parameters != null && member.Parameters.Count > 0) {
2679 xpath.Append ("/Parameters[count(Parameter) = ")
2680 .Append (member.Parameters.Count);
2681 for (int i = 0; i < member.Parameters.Count; ++i) {
2682 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2683 xpath.Append (member.Parameters [i]);
2684 xpath.Append ("\"");
2686 xpath.Append ("]/..");
2688 return xpath.ToString ();
2691 public static string GetXPathForMember (XPathNavigator member)
2693 StringBuilder xpath = new StringBuilder ();
2694 xpath.Append ("//Type[@FullName=\"")
2695 .Append (member.SelectSingleNode ("../../@FullName").Value)
2697 xpath.Append ("Members/Member[@MemberName=\"")
2698 .Append (member.SelectSingleNode ("@MemberName").Value)
2700 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2701 if (parameters.Count > 0) {
2702 xpath.Append ("/Parameters[count(Parameter) = ")
2703 .Append (parameters.Count);
2705 while (parameters.MoveNext ()) {
2707 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2708 xpath.Append (parameters.Current.Value);
2709 xpath.Append ("\"");
2711 xpath.Append ("]/..");
2713 return xpath.ToString ();
2716 public static string GetXPathForMember (MemberReference member)
2718 StringBuilder xpath = new StringBuilder ();
2719 xpath.Append ("//Type[@FullName=\"")
2720 .Append (member.DeclaringType.FullName)
2722 xpath.Append ("Members/Member[@MemberName=\"")
2723 .Append (GetMemberName (member))
2726 IList<ParameterDefinition> parameters = null;
2727 if (member is MethodDefinition)
2728 parameters = ((MethodDefinition) member).Parameters;
2729 else if (member is PropertyDefinition) {
2730 parameters = ((PropertyDefinition) member).Parameters;
2732 if (parameters != null && parameters.Count > 0) {
2733 xpath.Append ("/Parameters[count(Parameter) = ")
2734 .Append (parameters.Count);
2735 for (int i = 0; i < parameters.Count; ++i) {
2736 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2737 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2738 xpath.Append ("\"");
2740 xpath.Append ("]/..");
2742 return xpath.ToString ();
2746 static class CecilExtensions {
2747 public static string GetDeclaringType(this CustomAttribute attribute)
2749 var type = attribute.Constructor.DeclaringType;
2750 var typeName = type.FullName;
2752 string translatedType = NativeTypeManager.GetTranslatedName (type);
2753 return translatedType;
2756 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
2758 foreach (var c in type.Methods.Where (m => m.IsConstructor))
2759 yield return (MemberReference) c;
2760 foreach (var e in type.Events)
2761 yield return (MemberReference) e;
2762 foreach (var f in type.Fields)
2763 yield return (MemberReference) f;
2764 foreach (var m in type.Methods.Where (m => !m.IsConstructor))
2765 yield return (MemberReference) m;
2766 foreach (var t in type.NestedTypes)
2767 yield return (MemberReference) t;
2768 foreach (var p in type.Properties)
2769 yield return (MemberReference) p;
2772 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
2774 return GetMembers (type).Where (m => m.Name == member);
2777 public static MemberReference GetMember (this TypeDefinition type, string member)
2779 return GetMembers (type, member).EnsureZeroOrOne ();
2782 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2784 if (source.Count () > 1)
2785 throw new InvalidOperationException ("too many matches");
2786 return source.FirstOrDefault ();
2789 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2792 .Where (m => m.Name == method)
2793 .EnsureZeroOrOne ();
2796 public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
2798 TypeDefinition def = type as TypeDefinition;
2800 return new MemberReference [0];
2801 CustomAttribute defMemberAttr = def.CustomAttributes
2802 .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
2803 if (defMemberAttr == null)
2804 return new MemberReference [0];
2805 string name = (string) defMemberAttr.ConstructorArguments [0].Value;
2806 return def.Properties
2807 .Where (p => p.Name == name)
2808 .Select (p => (MemberReference) p);
2811 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2813 return assembly.Modules.SelectMany (md => md.GetAllTypes ());
2816 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2818 return GetTypes (assembly)
2819 .Where (td => td.FullName == type)
2820 .EnsureZeroOrOne ();
2823 public static bool IsGenericType (this TypeReference type)
2825 return type.GenericParameters.Count > 0;
2828 public static bool IsGenericMethod (this MethodReference method)
2830 return method.GenericParameters.Count > 0;
2833 public static MemberReference Resolve (this MemberReference member)
2835 FieldReference fr = member as FieldReference;
2837 return fr.Resolve ();
2838 MethodReference mr = member as MethodReference;
2840 return mr.Resolve ();
2841 TypeReference tr = member as TypeReference;
2843 return tr.Resolve ();
2844 PropertyReference pr = member as PropertyReference;
2847 EventReference er = member as EventReference;
2850 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2853 public static TypeReference GetUnderlyingType (this TypeDefinition type)
2857 return type.Fields.First (f => f.Name == "value__").FieldType;
2860 public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
2862 return self.Types.SelectMany (t => t.GetAllTypes ());
2865 static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
2869 if (!self.HasNestedTypes)
2872 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
2882 static class DocUtils {
2884 public static bool DoesNotHaveApiStyle(this XmlElement element, ApiStyle style) {
2885 string styleString = style.ToString ().ToLower ();
2886 string apistylevalue = element.GetAttribute ("apistyle");
2887 return apistylevalue != styleString || string.IsNullOrWhiteSpace(apistylevalue);
2889 public static bool HasApiStyle(this XmlElement element, ApiStyle style) {
2890 string styleString = style.ToString ().ToLower ();
2891 return element.GetAttribute ("apistyle") == styleString;
2893 public static void AddApiStyle(this XmlElement element, ApiStyle style) {
2894 string styleString = style.ToString ().ToLower ();
2895 var existingValue = element.GetAttribute ("apistyle");
2896 if (string.IsNullOrWhiteSpace (existingValue) || existingValue != styleString) {
2897 element.SetAttribute ("apistyle", styleString);
2901 public static bool IsExplicitlyImplemented (MethodDefinition method)
2903 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2906 public static string GetTypeDotMember (string name)
2908 int startType, startMethod;
2909 startType = startMethod = -1;
2910 for (int i = 0; i < name.Length; ++i) {
2911 if (name [i] == '.') {
2912 startType = startMethod;
2916 return name.Substring (startType+1);
2919 public static string GetMember (string name)
2921 int i = name.LastIndexOf ('.');
2924 return name.Substring (i+1);
2927 public static void GetInfoForExplicitlyImplementedMethod (
2928 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2932 if (method.Overrides.Count != 1)
2933 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2934 iface = method.Overrides [0].DeclaringType;
2935 ifaceMethod = method.Overrides [0];
2938 public static string GetPropertyName (PropertyDefinition pi)
2940 // Issue: (g)mcs-generated assemblies that explicitly implement
2941 // properties don't specify the full namespace, just the
2942 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2943 MethodDefinition method = pi.GetMethod;
2945 method = pi.SetMethod;
2946 if (!IsExplicitlyImplemented (method))
2949 // Need to determine appropriate namespace for this member.
2950 TypeReference iface;
2951 MethodReference ifaceMethod;
2952 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2953 return string.Join (".", new string[]{
2954 DocTypeFullMemberFormatter.Default.GetName (iface),
2955 GetMember (pi.Name)});
2958 public static string GetNamespace (TypeReference type)
2960 if (type.GetElementType ().IsNested)
2961 type = type.GetElementType ();
2962 while (type != null && type.IsNested)
2963 type = type.DeclaringType;
2965 return string.Empty;
2967 string typeNS = type.Namespace;
2969 // first, make sure this isn't a type reference to another assembly/module
2971 bool isInAssembly = MDocUpdater.IsInAssemblies(type.Module.Name);
2972 if (isInAssembly && !typeNS.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (type)) {
2973 typeNS = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typeNS);
2978 public static string PathCombine (string dir, string path)
2984 return Path.Combine (dir, path);
2987 public static bool IsExtensionMethod (MethodDefinition method)
2990 method.CustomAttributes
2991 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2992 && method.DeclaringType.CustomAttributes
2993 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
2996 public static bool IsDelegate (TypeDefinition type)
2998 TypeReference baseRef = type.BaseType;
2999 if (baseRef == null)
3001 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
3002 baseRef.FullName == "System.MulticastDelegate";
3005 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
3007 List<TypeReference> decls = new List<TypeReference> ();
3009 while (type.DeclaringType != null) {
3010 decls.Add (type.DeclaringType);
3011 type = type.DeclaringType;
3017 public static int GetGenericArgumentCount (TypeReference type)
3019 GenericInstanceType inst = type as GenericInstanceType;
3021 ? inst.GenericArguments.Count
3022 : type.GenericParameters.Count;
3025 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
3027 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
3028 List<TypeReference> userInterfaces = new List<TypeReference> ();
3029 foreach (TypeReference iface in type.Interfaces) {
3030 TypeReference lookup = iface.Resolve () ?? iface;
3031 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
3032 userInterfaces.Add (iface);
3034 return userInterfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()));
3037 private static string GetQualifiedTypeName (TypeReference type)
3039 return "[" + type.Scope.Name + "]" + type.FullName;
3042 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
3044 HashSet<string> inheritedInterfaces = new HashSet<string> ();
3045 Action<TypeDefinition> a = null;
3047 if (t == null) return;
3048 foreach (TypeReference r in t.Interfaces) {
3049 inheritedInterfaces.Add (GetQualifiedTypeName (r));
3053 TypeReference baseRef = type.BaseType;
3054 while (baseRef != null) {
3055 TypeDefinition baseDef = baseRef.Resolve ();
3056 if (baseDef != null) {
3058 baseRef = baseDef.BaseType;
3063 foreach (TypeReference r in type.Interfaces)
3065 return inheritedInterfaces;
3069 class DocsNodeInfo {
3070 public DocsNodeInfo (XmlElement node)
3075 public DocsNodeInfo (XmlElement node, TypeDefinition type)
3081 public DocsNodeInfo (XmlElement node, MemberReference member)
3084 SetMemberInfo (member);
3087 void SetType (TypeDefinition type)
3090 throw new ArgumentNullException ("type");
3092 GenericParameters = new List<GenericParameter> (type.GenericParameters);
3093 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
3094 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
3095 for (int i = 0; i < declTypes.Count - 1; ++i) {
3096 int remove = System.Math.Min (maxGenArgs,
3097 DocUtils.GetGenericArgumentCount (declTypes [i]));
3098 maxGenArgs -= remove;
3099 while (remove-- > 0)
3100 GenericParameters.RemoveAt (0);
3102 if (DocUtils.IsDelegate (type)) {
3103 Parameters = type.GetMethod("Invoke").Parameters;
3104 ReturnType = type.GetMethod("Invoke").ReturnType;
3105 ReturnIsReturn = true;
3109 void SetMemberInfo (MemberReference member)
3112 throw new ArgumentNullException ("member");
3113 ReturnIsReturn = true;
3117 if (member is MethodReference ) {
3118 MethodReference mr = (MethodReference) member;
3119 Parameters = mr.Parameters;
3120 if (mr.IsGenericMethod ()) {
3121 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
3124 else if (member is PropertyDefinition) {
3125 Parameters = ((PropertyDefinition) member).Parameters;
3128 if (member is MethodDefinition) {
3129 ReturnType = ((MethodDefinition) member).ReturnType;
3130 } else if (member is PropertyDefinition) {
3131 ReturnType = ((PropertyDefinition) member).PropertyType;
3132 ReturnIsReturn = false;
3135 // no remarks section for enum members
3136 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
3140 public TypeReference ReturnType;
3141 public List<GenericParameter> GenericParameters;
3142 public IList<ParameterDefinition> Parameters;
3143 public bool ReturnIsReturn;
3144 public XmlElement Node;
3145 public bool AddRemarks = true;
3146 public MemberReference Member;
3147 public TypeDefinition Type;
3149 public override string ToString ()
3151 return string.Format ("{0} - {1} - {2}", Type, Member, Node == null ? "no xml" : "with xml");
3155 class DocumentationEnumerator {
3157 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3159 return GetDocumentationTypes (assembly, forTypes, null);
3162 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3164 foreach (TypeDefinition type in assembly.GetTypes()) {
3165 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
3167 if (seen != null && seen.Contains (type.FullName))
3170 foreach (TypeDefinition nested in type.NestedTypes)
3171 yield return nested;
3175 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3177 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
3178 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
3179 oldmember.RemoveAttribute ("__monodocer-seen__");
3182 MemberReference m = GetMember (type, new DocumentationMember (oldmember));
3184 yield return new DocsNodeInfo (oldmember);
3187 yield return new DocsNodeInfo (oldmember, m);
3192 protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
3194 string membertype = member.MemberType;
3196 string returntype = member.ReturnType;
3198 string docName = member.MemberName;
3200 string[] docTypeParams = GetTypeParameters (docName);
3202 // If we're using 'magic types', then we might get false positives ... in those cases, we keep searching
3203 MemberReference likelyCandidate = null;
3205 // Loop through all members in this type with the same name
3206 var reflectedMembers = GetReflectionMembers (type, docName).ToArray ();
3207 foreach (MemberReference mi in reflectedMembers) {
3208 bool matchedMagicType = false;
3209 if (mi is TypeDefinition) continue;
3210 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
3212 if (MDocUpdater.IsPrivate (mi))
3215 IList<ParameterDefinition> pis = null;
3216 string[] typeParams = null;
3217 if (mi is MethodDefinition) {
3218 MethodDefinition mb = (MethodDefinition) mi;
3219 pis = mb.Parameters;
3220 if (mb.IsGenericMethod ()) {
3221 IList<GenericParameter> args = mb.GenericParameters;
3222 typeParams = args.Select (p => p.Name).ToArray ();
3225 else if (mi is PropertyDefinition)
3226 pis = ((PropertyDefinition)mi).Parameters;
3228 // check type parameters
3229 int methodTcount = member.TypeParameters == null ? 0 : member.TypeParameters.Count;
3230 int reflectionTcount = typeParams == null ? 0 : typeParams.Length;
3231 if (methodTcount != reflectionTcount)
3234 // check member parameters
3235 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
3236 int pcount = pis == null ? 0 : pis.Count;
3237 if (mcount != pcount)
3240 MethodDefinition mDef = mi as MethodDefinition;
3241 if (mDef != null && !mDef.IsConstructor) {
3242 // Casting operators can overload based on return type.
3243 string rtype = GetReplacedString (
3244 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
3245 typeParams, docTypeParams);
3246 string originalRType = rtype;
3247 if (MDocUpdater.SwitchingToMagicTypes) {
3248 rtype = NativeTypeManager.ConvertFromNativeType (rtype);
3251 if ((returntype != rtype && originalRType == rtype) ||
3252 (MDocUpdater.SwitchingToMagicTypes && returntype != originalRType && returntype != rtype && originalRType != rtype)) {
3256 if (originalRType != rtype)
3257 matchedMagicType = true;
3263 for (int i = 0; i < pis.Count; i++) {
3264 string paramType = GetReplacedString (
3265 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
3266 typeParams, docTypeParams);
3268 // if magictypes, replace paramType to "classic value" ... so the comparison works
3269 string originalParamType = paramType;
3270 if (MDocUpdater.SwitchingToMagicTypes) {
3271 paramType = NativeTypeManager.ConvertFromNativeType (paramType);
3274 string xmlMemberType = member.Parameters [i];
3275 if ((!paramType.Equals(xmlMemberType) && paramType.Equals(originalParamType)) ||
3276 (MDocUpdater.SwitchingToMagicTypes && !originalParamType.Equals(xmlMemberType) && !paramType.Equals(xmlMemberType) && !paramType.Equals(originalParamType))) {
3278 // did not match ... if we're dropping the namespace, and the paramType has the dropped
3279 // namespace, we should see if it matches when added
3280 bool stillDoesntMatch = true;
3281 if (MDocUpdater.HasDroppedNamespace(type) && paramType.StartsWith (MDocUpdater.droppedNamespace)) {
3282 string withDroppedNs = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, xmlMemberType);
3284 stillDoesntMatch = withDroppedNs != paramType;
3287 if (stillDoesntMatch) {
3293 if (originalParamType != paramType)
3294 matchedMagicType = true;
3296 if (!good) continue;
3298 if (MDocUpdater.SwitchingToMagicTypes && likelyCandidate == null && matchedMagicType) {
3299 // 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
3300 likelyCandidate = mi;
3307 return likelyCandidate;
3310 static string[] GetTypeParameters (string docName)
3312 if (docName [docName.Length-1] != '>')
3314 StringList types = new StringList ();
3315 int endToken = docName.Length-2;
3316 int i = docName.Length-2;
3318 if (docName [i] == ',' || docName [i] == '<') {
3319 types.Add (docName.Substring (i + 1, endToken - i));
3322 if (docName [i] == '<')
3327 return types.ToArray ();
3330 protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
3332 // In case of dropping the namespace, we have to remove the dropped NS
3333 // so that docName will match what's in the assembly/type
3334 if (MDocUpdater.HasDroppedNamespace (type) && docName.StartsWith(MDocUpdater.droppedNamespace + ".")) {
3335 int droppedNsLength = MDocUpdater.droppedNamespace.Length;
3336 docName = docName.Substring (droppedNsLength + 1, docName.Length - droppedNsLength - 1);
3339 // need to worry about 4 forms of //@MemberName values:
3340 // 1. "Normal" (non-generic) member names: GetEnumerator
3342 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
3343 // - try as-is, and try type.member (due to "kludge" for property
3345 // 3. "Normal" Generic member names: Sort<T> (CSC)
3346 // - need to remove generic parameters --> "Sort"
3347 // 4. Explicitly-implemented interface members for generic interfaces:
3348 // -- System.Collections.Generic.IEnumerable<T>.Current
3349 // - Try as-is, and try type.member, *keeping* the generic parameters.
3350 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
3351 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
3352 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
3353 // this as (1) or (2).
3354 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
3356 foreach (MemberReference mi in type.GetMembers (docName))
3358 if (CountChars (docName, '.') > 0)
3359 // might be a property; try only type.member instead of
3360 // namespace.type.member.
3361 foreach (MemberReference mi in
3362 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
3369 int startLt, startType, startMethod;
3370 startLt = startType = startMethod = -1;
3371 for (int i = 0; i < docName.Length; ++i) {
3372 switch (docName [i]) {
3381 if (numLt == 0 && (i + 1) < docName.Length)
3382 // there's another character in docName, so this <...> sequence is
3383 // probably part of a generic type -- case 4.
3387 startType = startMethod;
3393 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
3395 foreach (MemberReference mi in type.GetMembers (refName))
3399 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
3402 // If we _still_ haven't found it, we've hit another generic naming issue:
3403 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
3404 // explicitly-implemented METHOD names (not properties), e.g.
3405 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
3406 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
3407 // which the XML docs will contain.
3409 // Alas, we can't derive the Mono name from docName, so we need to iterate
3410 // over all member names, convert them into CSC format, and compare... :-(
3413 foreach (MemberReference mi in type.GetMembers ()) {
3414 if (MDocUpdater.GetMemberName (mi) == docName)
3419 static string GetReplacedString (string typeName, string[] from, string[] to)
3423 for (int i = 0; i < from.Length; ++i)
3424 typeName = typeName.Replace (from [i], to [i]);
3428 private static int CountChars (string s, char c)
3431 for (int i = 0; i < s.Length; ++i) {
3439 class EcmaDocumentationEnumerator : DocumentationEnumerator {
3444 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
3447 this.ecmadocs = ecmaDocs;
3450 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3452 HashSet<string> seen = new HashSet<string> ();
3453 return GetDocumentationTypes (assembly, forTypes, seen)
3454 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
3457 new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3460 while (ecmadocs.Read ()) {
3461 switch (ecmadocs.Name) {
3463 if (typeDepth == -1)
3464 typeDepth = ecmadocs.Depth;
3465 if (ecmadocs.NodeType != XmlNodeType.Element)
3467 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
3469 string typename = ecmadocs.GetAttribute ("FullName");
3470 string typename2 = MDocUpdater.GetTypeFileName (typename);
3471 if (forTypes != null &&
3472 forTypes.BinarySearch (typename) < 0 &&
3473 typename != typename2 &&
3474 forTypes.BinarySearch (typename2) < 0)
3477 if ((t = assembly.GetType (typename)) == null &&
3478 (t = assembly.GetType (typename2)) == null)
3480 seen.Add (typename);
3481 if (typename != typename2)
3482 seen.Add (typename2);
3483 Console.WriteLine (" Import: {0}", t.FullName);
3484 if (ecmadocs.Name != "Docs") {
3485 int depth = ecmadocs.Depth;
3486 while (ecmadocs.Read ()) {
3487 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
3491 if (!ecmadocs.IsStartElement ("Docs"))
3492 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
3502 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3504 return GetMembers (basefile, type)
3505 .Concat (base.GetDocumentationMembers (basefile, type));
3508 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
3510 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
3513 if (ecmadocs.IsEmptyElement)
3516 int membersDepth = ecmadocs.Depth;
3518 while (go && ecmadocs.Read ()) {
3519 switch (ecmadocs.Name) {
3521 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
3523 DocumentationMember dm = new DocumentationMember (ecmadocs);
3525 string xp = MDocUpdater.GetXPathForMember (dm);
3526 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
3528 if (oldmember == null) {
3529 m = GetMember (type, dm);
3531 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3532 type.FullName, dm.MemberSignatures ["C#"]);
3533 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
3536 // oldmember lookup may have failed due to type parameter renames.
3538 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
3539 if (oldmember == null) {
3540 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
3541 oldmember = basefile.CreateElement ("Member");
3542 oldmember.SetAttribute ("MemberName", dm.MemberName);
3543 members.AppendChild (oldmember);
3544 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
3545 XmlElement ms = basefile.CreateElement ("MemberSignature");
3546 ms.SetAttribute ("Language", key);
3547 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
3548 oldmember.AppendChild (ms);
3550 oldmember.SetAttribute ("__monodocer-seen__", "true");
3551 Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
3556 m = GetMember (type, new DocumentationMember (oldmember));
3558 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3559 type.FullName, dm.MemberSignatures ["C#"]);
3562 oldmember.SetAttribute ("__monodocer-seen__", "true");
3564 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
3565 if (ecmadocs.Name != "Docs")
3566 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
3571 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
3580 abstract class DocumentationImporter {
3582 public abstract void ImportDocumentation (DocsNodeInfo info);
3585 class MsxdocDocumentationImporter : DocumentationImporter {
3587 XmlDocument slashdocs;
3589 public MsxdocDocumentationImporter (string file)
3591 var xml = File.ReadAllText (file);
3593 // Ensure Unix line endings
3594 xml = xml.Replace ("\r", "");
3596 slashdocs = new XmlDocument();
3597 slashdocs.LoadXml (xml);
3600 public override void ImportDocumentation (DocsNodeInfo info)
3602 XmlNode elem = GetDocs (info.Member ?? info.Type);
3607 XmlElement e = info.Node;
3609 if (elem.SelectSingleNode("summary") != null)
3610 MDocUpdater.ClearElement(e, "summary");
3611 if (elem.SelectSingleNode("remarks") != null)
3612 MDocUpdater.ClearElement(e, "remarks");
3613 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3614 MDocUpdater.ClearElement(e, "value");
3615 MDocUpdater.ClearElement(e, "returns");
3618 foreach (XmlNode child in elem.ChildNodes) {
3619 switch (child.Name) {
3622 XmlAttribute name = child.Attributes ["name"];
3625 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3627 p2.InnerXml = child.InnerXml;
3630 // Occasionally XML documentation will use <returns/> on
3631 // properties, so let's try to normalize things.
3634 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3635 v.InnerXml = child.InnerXml;
3641 case "permission": {
3642 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3645 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3647 a = e.OwnerDocument.CreateElement (child.Name);
3648 a.SetAttribute ("cref", cref.Value);
3651 a.InnerXml = child.InnerXml;
3655 XmlAttribute cref = child.Attributes ["cref"];
3658 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3660 a = e.OwnerDocument.CreateElement ("altmember");
3661 a.SetAttribute ("cref", cref.Value);
3668 if (child.NodeType == XmlNodeType.Element &&
3669 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3672 MDocUpdater.CopyNode (child, e);
3679 private XmlNode GetDocs (MemberReference member)
3681 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3682 if (slashdocsig != null)
3683 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3688 class EcmaDocumentationImporter : DocumentationImporter {
3692 public EcmaDocumentationImporter (XmlReader ecmaDocs)
3694 this.ecmadocs = ecmaDocs;
3697 public override void ImportDocumentation (DocsNodeInfo info)
3699 if (!ecmadocs.IsStartElement ("Docs")) {
3703 XmlElement e = info.Node;
3705 int depth = ecmadocs.Depth;
3706 ecmadocs.ReadStartElement ("Docs");
3707 while (ecmadocs.Read ()) {
3708 if (ecmadocs.Name == "Docs") {
3709 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3712 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3714 if (!ecmadocs.IsStartElement ())
3716 switch (ecmadocs.Name) {
3719 string name = ecmadocs.GetAttribute ("name");
3722 XmlNode doc = e.SelectSingleNode (
3723 ecmadocs.Name + "[@name='" + name + "']");
3724 string value = ecmadocs.ReadInnerXml ();
3726 doc.InnerXml = value.Replace ("\r", "");
3733 string name = ecmadocs.Name;
3734 string cref = ecmadocs.GetAttribute ("cref");
3737 XmlNode doc = e.SelectSingleNode (
3738 ecmadocs.Name + "[@cref='" + cref + "']");
3739 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3741 doc.InnerXml = value;
3743 XmlElement n = e.OwnerDocument.CreateElement (name);
3744 n.SetAttribute ("cref", cref);
3751 string name = ecmadocs.Name;
3752 string xpath = ecmadocs.Name;
3753 StringList attributes = new StringList (ecmadocs.AttributeCount);
3754 if (ecmadocs.MoveToFirstAttribute ()) {
3756 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3757 } while (ecmadocs.MoveToNextAttribute ());
3758 ecmadocs.MoveToContent ();
3760 if (attributes.Count > 0) {
3761 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3763 XmlNode doc = e.SelectSingleNode (xpath);
3764 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3766 doc.InnerXml = value;
3769 XmlElement n = e.OwnerDocument.CreateElement (name);
3771 foreach (string a in attributes) {
3772 int eq = a.IndexOf ('=');
3773 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3784 class DocumentationMember {
3785 public StringToStringMap MemberSignatures = new StringToStringMap ();
3786 public string ReturnType;
3787 public StringList Parameters;
3788 public StringList TypeParameters;
3789 public string MemberName;
3790 public string MemberType;
3792 public DocumentationMember (XmlReader reader)
3794 MemberName = reader.GetAttribute ("MemberName");
3795 int depth = reader.Depth;
3797 StringList p = new StringList ();
3798 StringList tp = new StringList ();
3800 if (reader.NodeType != XmlNodeType.Element)
3803 bool shouldUse = true;
3805 string apistyle = reader.GetAttribute ("apistyle");
3806 shouldUse = string.IsNullOrWhiteSpace(apistyle) || apistyle == "classic"; // only use this tag if it's an 'classic' style node
3808 catch (Exception ex) {}
3809 switch (reader.Name) {
3810 case "MemberSignature":
3812 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3816 MemberType = reader.ReadElementString ();
3819 if (reader.Depth == depth + 2 && shouldUse)
3820 ReturnType = reader.ReadElementString ();
3823 if (reader.Depth == depth + 2 && shouldUse)
3824 p.Add (reader.GetAttribute ("Type"));
3826 case "TypeParameter":
3827 if (reader.Depth == depth + 2 && shouldUse)
3828 tp.Add (reader.GetAttribute ("Name"));
3831 if (reader.Depth == depth + 1)
3835 } while (go && reader.Read () && reader.Depth >= depth);
3840 TypeParameters = tp;
3842 DiscernTypeParameters ();
3846 public DocumentationMember (XmlNode node)
3848 MemberName = node.Attributes ["MemberName"].Value;
3849 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3850 XmlAttribute l = n.Attributes ["Language"];
3851 XmlAttribute v = n.Attributes ["Value"];
3852 XmlAttribute apistyle = n.Attributes ["apistyle"];
3853 bool shouldUse = apistyle == null || apistyle.Value == "classic";
3854 if (l != null && v != null && shouldUse)
3855 MemberSignatures [l.Value] = v.Value;
3857 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3858 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType[not(@apistyle) or @apistyle='classic']");
3860 ReturnType = rt.InnerText;
3861 XmlNodeList p = node.SelectNodes ("Parameters/Parameter[not(@apistyle) or @apistyle='classic']");
3863 Parameters = new StringList (p.Count);
3864 for (int i = 0; i < p.Count; ++i)
3865 Parameters.Add (p [i].Attributes ["Type"].Value);
3867 XmlNodeList tp = node.SelectNodes ("TypeParameters/TypeParameter[not(@apistyle) or @apistyle='classic']");
3869 TypeParameters = new StringList (tp.Count);
3870 for (int i = 0; i < tp.Count; ++i)
3871 TypeParameters.Add (tp [i].Attributes ["Name"].Value);
3874 DiscernTypeParameters ();
3878 void DiscernTypeParameters ()
3880 // see if we can discern the param list from the name
3881 if (MemberName.Contains ("<") && MemberName.EndsWith (">")) {
3882 var starti = MemberName.IndexOf ("<") + 1;
3883 var endi = MemberName.LastIndexOf (">");
3884 var paramlist = MemberName.Substring (starti, endi - starti);
3885 var tparams = paramlist.Split (new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
3886 TypeParameters = new StringList (tparams);
3891 public class DynamicParserContext {
3892 public ReadOnlyCollection<bool> TransformFlags;
3893 public int TransformIndex;
3895 public DynamicParserContext (ICustomAttributeProvider provider)
3898 if (provider.HasCustomAttributes &&
3899 (da = (provider.CustomAttributes.Cast<CustomAttribute>()
3900 .SingleOrDefault (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))) != null) {
3901 CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
3902 ? new CustomAttributeArgument [0]
3903 : (CustomAttributeArgument[]) da.ConstructorArguments [0].Value;
3905 TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool) t.Value).ToArray());
3910 public enum MemberFormatterState {
3912 WithinGenericTypeParameters,
3915 public abstract class MemberFormatter {
3917 public virtual string Language {
3921 public string GetName (MemberReference member)
3923 return GetName (member, null);
3926 public virtual string GetName (MemberReference member, DynamicParserContext context)
3928 TypeReference type = member as TypeReference;
3930 return GetTypeName (type, context);
3931 MethodReference method = member as MethodReference;
3932 if (method != null && method.Name == ".ctor") // method.IsConstructor
3933 return GetConstructorName (method);
3935 return GetMethodName (method);
3936 PropertyReference prop = member as PropertyReference;
3938 return GetPropertyName (prop);
3939 FieldReference field = member as FieldReference;
3941 return GetFieldName (field);
3942 EventReference e = member as EventReference;
3944 return GetEventName (e);
3945 throw new NotSupportedException ("Can't handle: " +
3946 (member == null ? "null" : member.GetType().ToString()));
3949 protected virtual string GetTypeName (TypeReference type)
3951 return GetTypeName (type, null);
3954 protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
3957 throw new ArgumentNullException ("type");
3958 return _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
3961 protected virtual char[] ArrayDelimeters {
3962 get {return new char[]{'[', ']'};}
3965 protected virtual MemberFormatterState MemberFormatterState { get; set; }
3967 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3969 if (type is ArrayType) {
3970 TypeSpecification spec = type as TypeSpecification;
3971 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
3972 return AppendArrayModifiers (buf, (ArrayType) type);
3974 if (type is ByReferenceType) {
3975 return AppendRefTypeName (buf, type, context);
3977 if (type is PointerType) {
3978 return AppendPointerTypeName (buf, type, context);
3980 if (type is GenericParameter) {
3981 return AppendTypeName (buf, type, context);
3983 AppendNamespace (buf, type);
3984 GenericInstanceType genInst = type as GenericInstanceType;
3985 if (type.GenericParameters.Count == 0 &&
3986 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3987 return AppendFullTypeName (buf, type, context);
3989 return AppendGenericType (buf, type, context);
3992 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3994 string ns = DocUtils.GetNamespace (type);
3995 if (ns != null && ns.Length > 0)
3996 buf.Append (ns).Append ('.');
4000 protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4002 if (type.DeclaringType != null)
4003 AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
4004 return AppendTypeName (buf, type, context);
4007 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4009 if (context != null)
4010 context.TransformIndex++;
4011 return AppendTypeName (buf, type.Name);
4014 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
4016 int n = typename.IndexOf ("`");
4018 return buf.Append (typename.Substring (0, n));
4019 return buf.Append (typename);
4022 protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
4024 buf.Append (ArrayDelimeters [0]);
4025 int rank = array.Rank;
4027 buf.Append (new string (',', rank-1));
4028 return buf.Append (ArrayDelimeters [1]);
4031 protected virtual string RefTypeModifier {
4035 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4037 TypeSpecification spec = type as TypeSpecification;
4038 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4039 .Append (RefTypeModifier);
4042 protected virtual string PointerModifier {
4046 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4048 TypeSpecification spec = type as TypeSpecification;
4049 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4050 .Append (PointerModifier);
4053 protected virtual char[] GenericTypeContainer {
4054 get {return new char[]{'<', '>'};}
4057 protected virtual char NestedTypeSeparator {
4061 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4063 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4064 type is GenericInstanceType ? type.GetElementType () : type);
4065 List<TypeReference> genArgs = GetGenericArguments (type);
4068 bool insertNested = false;
4069 foreach (var decl in decls) {
4070 TypeReference declDef = decl.Resolve () ?? decl;
4072 buf.Append (NestedTypeSeparator);
4074 insertNested = true;
4075 AppendTypeName (buf, declDef, context);
4076 int ac = DocUtils.GetGenericArgumentCount (declDef);
4080 buf.Append (GenericTypeContainer [0]);
4081 var origState = MemberFormatterState;
4082 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4083 _AppendTypeName (buf, genArgs [argIdx++], context);
4084 for (int i = 1; i < c; ++i) {
4085 _AppendTypeName (buf.Append (","), genArgs [argIdx++], context);
4087 MemberFormatterState = origState;
4088 buf.Append (GenericTypeContainer [1]);
4094 protected List<TypeReference> GetGenericArguments (TypeReference type)
4096 var args = new List<TypeReference> ();
4097 GenericInstanceType inst = type as GenericInstanceType;
4099 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
4101 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
4105 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4110 protected virtual string GetConstructorName (MethodReference constructor)
4112 return constructor.Name;
4115 protected virtual string GetMethodName (MethodReference method)
4120 protected virtual string GetPropertyName (PropertyReference property)
4122 return property.Name;
4125 protected virtual string GetFieldName (FieldReference field)
4130 protected virtual string GetEventName (EventReference e)
4135 public virtual string GetDeclaration (MemberReference member)
4138 throw new ArgumentNullException ("member");
4139 TypeDefinition type = member as TypeDefinition;
4141 return GetTypeDeclaration (type);
4142 MethodDefinition method = member as MethodDefinition;
4143 if (method != null && method.IsConstructor)
4144 return GetConstructorDeclaration (method);
4146 return GetMethodDeclaration (method);
4147 PropertyDefinition prop = member as PropertyDefinition;
4149 return GetPropertyDeclaration (prop);
4150 FieldDefinition field = member as FieldDefinition;
4152 return GetFieldDeclaration (field);
4153 EventDefinition e = member as EventDefinition;
4155 return GetEventDeclaration (e);
4156 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
4159 protected virtual string GetTypeDeclaration (TypeDefinition type)
4162 throw new ArgumentNullException ("type");
4163 StringBuilder buf = new StringBuilder (type.Name.Length);
4164 _AppendTypeName (buf, type, null);
4165 AppendGenericTypeConstraints (buf, type);
4166 return buf.ToString ();
4169 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
4171 return GetConstructorName (constructor);
4174 protected virtual string GetMethodDeclaration (MethodDefinition method)
4176 if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
4177 ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
4180 // Special signature for destructors.
4181 if (method.Name == "Finalize" && method.Parameters.Count == 0)
4182 return GetFinalizerName (method);
4184 StringBuilder buf = new StringBuilder ();
4186 AppendVisibility (buf, method);
4187 if (buf.Length == 0 &&
4188 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
4191 AppendModifiers (buf, method);
4193 if (buf.Length != 0)
4195 buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
4197 AppendMethodName (buf, method);
4198 AppendGenericMethod (buf, method).Append (" ");
4199 AppendParameters (buf, method, method.Parameters);
4200 AppendGenericMethodConstraints (buf, method);
4201 return buf.ToString ();
4204 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4206 return buf.Append (method.Name);
4209 protected virtual string GetFinalizerName (MethodDefinition method)
4214 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4219 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4224 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4229 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4234 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4239 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
4241 return GetPropertyName (property);
4244 protected virtual string GetFieldDeclaration (FieldDefinition field)
4246 return GetFieldName (field);
4249 protected virtual string GetEventDeclaration (EventDefinition e)
4251 return GetEventName (e);
4255 class ILFullMemberFormatter : MemberFormatter {
4257 public override string Language {
4258 get {return "ILAsm";}
4261 protected override char NestedTypeSeparator {
4267 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4269 if (GetBuiltinType (type.FullName) != null)
4271 string ns = DocUtils.GetNamespace (type);
4272 if (ns != null && ns.Length > 0) {
4273 if (type.IsValueType)
4274 buf.Append ("valuetype ");
4276 buf.Append ("class ");
4277 buf.Append (ns).Append ('.');
4282 protected static string GetBuiltinType (string t)
4285 case "System.Byte": return "unsigned int8";
4286 case "System.SByte": return "int8";
4287 case "System.Int16": return "int16";
4288 case "System.Int32": return "int32";
4289 case "System.Int64": return "int64";
4290 case "System.IntPtr": return "native int";
4292 case "System.UInt16": return "unsigned int16";
4293 case "System.UInt32": return "unsigned int32";
4294 case "System.UInt64": return "unsigned int64";
4295 case "System.UIntPtr": return "native unsigned int";
4297 case "System.Single": return "float32";
4298 case "System.Double": return "float64";
4299 case "System.Boolean": return "bool";
4300 case "System.Char": return "char";
4301 case "System.Void": return "void";
4302 case "System.String": return "string";
4303 case "System.Object": return "object";
4308 protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
4310 return buf.Append (typename);
4313 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4315 if (type is GenericParameter) {
4316 AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
4320 string s = GetBuiltinType (type.FullName);
4322 return buf.Append (s);
4324 return base.AppendTypeName (buf, type, context);
4327 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
4329 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
4330 return buf.Append (type.Owner is TypeReference ? "!" : "!!");
4332 GenericParameterAttributes attrs = type.Attributes;
4333 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
4334 buf.Append ("class ");
4335 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
4336 buf.Append ("struct ");
4337 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
4338 buf.Append (".ctor ");
4339 IList<TypeReference> constraints = type.Constraints;
4340 MemberFormatterState = 0;
4341 if (constraints.Count > 0) {
4342 var full = new ILFullMemberFormatter ();
4343 buf.Append ("(").Append (full.GetName (constraints [0]));
4344 for (int i = 1; i < constraints.Count; ++i) {
4345 buf.Append (", ").Append (full.GetName (constraints [i]));
4349 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4351 if ((attrs & GenericParameterAttributes.Covariant) != 0)
4353 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
4358 protected override string GetTypeDeclaration (TypeDefinition type)
4360 string visibility = GetTypeVisibility (type.Attributes);
4361 if (visibility == null)
4364 StringBuilder buf = new StringBuilder ();
4366 buf.Append (".class ");
4368 buf.Append ("nested ");
4369 buf.Append (visibility).Append (" ");
4370 if (type.IsInterface)
4371 buf.Append ("interface ");
4372 if (type.IsSequentialLayout)
4373 buf.Append ("sequential ");
4374 if (type.IsAutoLayout)
4375 buf.Append ("auto ");
4376 if (type.IsAnsiClass)
4377 buf.Append ("ansi ");
4378 if (type.IsAbstract)
4379 buf.Append ("abstract ");
4380 if (type.IsSerializable)
4381 buf.Append ("serializable ");
4383 buf.Append ("sealed ");
4384 if (type.IsBeforeFieldInit)
4385 buf.Append ("beforefieldinit ");
4386 var state = MemberFormatterState;
4387 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4388 buf.Append (GetName (type));
4389 MemberFormatterState = state;
4390 var full = new ILFullMemberFormatter ();
4391 if (type.BaseType != null) {
4392 buf.Append (" extends ");
4393 if (type.BaseType.FullName == "System.Object")
4394 buf.Append ("System.Object");
4396 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
4399 foreach (var name in type.Interfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()))
4400 .Select (i => full.GetName (i))
4401 .OrderBy (n => n)) {
4403 buf.Append (" implements ");
4412 return buf.ToString ();
4415 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4417 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4418 type is GenericInstanceType ? type.GetElementType () : type);
4420 foreach (var decl in decls) {
4421 TypeReference declDef = decl.Resolve () ?? decl;
4423 buf.Append (NestedTypeSeparator);
4426 AppendTypeName (buf, declDef, context);
4430 foreach (TypeReference arg in GetGenericArguments (type)) {
4434 _AppendTypeName (buf, arg, context);
4440 static string GetTypeVisibility (TypeAttributes ta)
4442 switch (ta & TypeAttributes.VisibilityMask) {
4443 case TypeAttributes.Public:
4444 case TypeAttributes.NestedPublic:
4447 case TypeAttributes.NestedFamily:
4448 case TypeAttributes.NestedFamORAssem:
4456 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4458 return GetMethodDeclaration (constructor);
4461 protected override string GetMethodDeclaration (MethodDefinition method)
4463 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
4466 var buf = new StringBuilder ();
4467 buf.Append (".method ");
4468 AppendVisibility (buf, method);
4469 if (method.IsStatic)
4470 buf.Append ("static ");
4471 if (method.IsHideBySig)
4472 buf.Append ("hidebysig ");
4473 if (method.IsPInvokeImpl) {
4474 var info = method.PInvokeInfo;
4475 buf.Append ("pinvokeimpl (\"")
4476 .Append (info.Module.Name)
4477 .Append ("\" as \"")
4478 .Append (info.EntryPoint)
4480 if (info.IsCharSetAuto)
4481 buf.Append (" auto");
4482 if (info.IsCharSetUnicode)
4483 buf.Append (" unicode");
4484 if (info.IsCharSetAnsi)
4485 buf.Append (" ansi");
4486 if (info.IsCallConvCdecl)
4487 buf.Append (" cdecl");
4488 if (info.IsCallConvStdCall)
4489 buf.Append (" stdcall");
4490 if (info.IsCallConvWinapi)
4491 buf.Append (" winapi");
4492 if (info.IsCallConvThiscall)
4493 buf.Append (" thiscall");
4494 if (info.SupportsLastError)
4495 buf.Append (" lasterr");
4498 if (method.IsSpecialName)
4499 buf.Append ("specialname ");
4500 if (method.IsRuntimeSpecialName)
4501 buf.Append ("rtspecialname ");
4502 if (method.IsNewSlot)
4503 buf.Append ("newslot ");
4504 if (method.IsVirtual)
4505 buf.Append ("virtual ");
4506 if (!method.IsStatic)
4507 buf.Append ("instance ");
4508 _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
4510 .Append (method.Name);
4511 if (method.IsGenericMethod ()) {
4512 var state = MemberFormatterState;
4513 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4514 IList<GenericParameter> args = method.GenericParameters;
4515 if (args.Count > 0) {
4517 _AppendTypeName (buf, args [0], null);
4518 for (int i = 1; i < args.Count; ++i)
4519 _AppendTypeName (buf.Append (", "), args [i], null);
4522 MemberFormatterState = state;
4527 for (int i = 0; i < method.Parameters.Count; ++i) {
4531 _AppendTypeName (buf, method.Parameters [i].ParameterType, new DynamicParserContext (method.Parameters [i]));
4533 buf.Append (method.Parameters [i].Name);
4537 buf.Append (" cil");
4538 if (method.IsRuntime)
4539 buf.Append (" runtime");
4540 if (method.IsManaged)
4541 buf.Append (" managed");
4543 return buf.ToString ();
4546 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4548 if (DocUtils.IsExplicitlyImplemented (method)) {
4549 TypeReference iface;
4550 MethodReference ifaceMethod;
4551 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4552 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4554 .Append (ifaceMethod.Name);
4556 return base.AppendMethodName (buf, method);
4559 protected override string RefTypeModifier {
4563 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4565 if (method.IsPublic)
4566 return buf.Append ("public ");
4567 if (method.IsFamilyAndAssembly)
4568 return buf.Append ("familyandassembly");
4569 if (method.IsFamilyOrAssembly)
4570 return buf.Append ("familyorassembly");
4571 if (method.IsFamily)
4572 return buf.Append ("family");
4576 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4578 string modifiers = String.Empty;
4579 if (method.IsStatic) modifiers += " static";
4580 if (method.IsVirtual && !method.IsAbstract) {
4581 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4582 else modifiers += " override";
4584 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4585 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4586 if (method.IsFinal) modifiers += " sealed";
4587 if (modifiers == " virtual sealed") modifiers = "";
4589 return buf.Append (modifiers);
4592 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4594 if (method.IsGenericMethod ()) {
4595 IList<GenericParameter> args = method.GenericParameters;
4596 if (args.Count > 0) {
4598 buf.Append (args [0].Name);
4599 for (int i = 1; i < args.Count; ++i)
4600 buf.Append (",").Append (args [i].Name);
4607 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4609 return AppendParameters (buf, method, parameters, '(', ')');
4612 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4616 if (parameters.Count > 0) {
4617 if (DocUtils.IsExtensionMethod (method))
4618 buf.Append ("this ");
4619 AppendParameter (buf, parameters [0]);
4620 for (int i = 1; i < parameters.Count; ++i) {
4622 AppendParameter (buf, parameters [i]);
4626 return buf.Append (end);
4629 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4631 if (parameter.ParameterType is ByReferenceType) {
4632 if (parameter.IsOut)
4633 buf.Append ("out ");
4635 buf.Append ("ref ");
4637 buf.Append (GetName (parameter.ParameterType)).Append (" ");
4638 return buf.Append (parameter.Name);
4641 protected override string GetPropertyDeclaration (PropertyDefinition property)
4643 MethodDefinition gm = null, sm = null;
4645 string get_visible = null;
4646 if ((gm = property.GetMethod) != null &&
4647 (DocUtils.IsExplicitlyImplemented (gm) ||
4648 (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
4649 get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
4650 string set_visible = null;
4651 if ((sm = property.SetMethod) != null &&
4652 (DocUtils.IsExplicitlyImplemented (sm) ||
4653 (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
4654 set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
4656 if ((set_visible == null) && (get_visible == null))
4659 StringBuilder buf = new StringBuilder ()
4660 .Append (".property ");
4661 if (!(gm ?? sm).IsStatic)
4662 buf.Append ("instance ");
4663 _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
4664 buf.Append (' ').Append (property.Name);
4665 if (!property.HasParameters || property.Parameters.Count == 0)
4666 return buf.ToString ();
4670 foreach (ParameterDefinition p in property.Parameters) {
4674 _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
4678 return buf.ToString ();
4681 protected override string GetFieldDeclaration (FieldDefinition field)
4683 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4684 if (declType.IsEnum && field.Name == "value__")
4685 return null; // This member of enums aren't documented.
4687 StringBuilder buf = new StringBuilder ();
4688 AppendFieldVisibility (buf, field);
4689 if (buf.Length == 0)
4692 buf.Insert (0, ".field ");
4695 buf.Append ("static ");
4696 if (field.IsInitOnly)
4697 buf.Append ("initonly ");
4698 if (field.IsLiteral)
4699 buf.Append ("literal ");
4700 _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
4701 buf.Append (' ').Append (field.Name);
4702 AppendFieldValue (buf, field);
4704 return buf.ToString ();
4707 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4710 return buf.Append ("public ");
4711 if (field.IsFamilyAndAssembly)
4712 return buf.Append ("familyandassembly ");
4713 if (field.IsFamilyOrAssembly)
4714 return buf.Append ("familyorassembly ");
4716 return buf.Append ("family ");
4720 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4722 // enums have a value__ field, which we ignore
4723 if (field.DeclaringType.IsGenericType ())
4725 if (field.HasConstant && field.IsLiteral) {
4728 val = field.Constant;
4733 buf.Append (" = ").Append ("null");
4734 else if (val is Enum)
4736 .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4738 .Append (val.ToString ())
4740 else if (val is IFormattable) {
4741 string value = ((IFormattable)val).ToString();
4744 buf.Append ("\"" + value + "\"");
4746 buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4755 protected override string GetEventDeclaration (EventDefinition e)
4757 StringBuilder buf = new StringBuilder ();
4758 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4763 buf.Append (".event ")
4764 .Append (GetName (e.EventType))
4768 return buf.ToString ();
4772 class ILMemberFormatter : ILFullMemberFormatter {
4773 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4779 class ILNativeTypeMemberFormatter : ILFullMemberFormatter {
4780 protected static string _GetBuiltinType (string t)
4782 //string moddedType = base.GetBuiltinType (t);
4784 //return moddedType;
4788 class CSharpNativeTypeMemberFormatter : CSharpFullMemberFormatter {
4789 protected override string GetCSharpType (string t) {
4790 string moddedType = base.GetCSharpType (t);
4792 switch (moddedType) {
4793 case "int": return "nint";
4798 case "System.Drawing.SizeF":
4799 return "CoreGraphics.CGSize";
4800 case "System.Drawing.PointF":
4801 return "CoreGraphics.CGPoint";
4802 case "System.Drawing.RectangleF":
4803 return "CoreGraphics.CGPoint";
4809 class CSharpFullMemberFormatter : MemberFormatter {
4811 public override string Language {
4815 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4818 string ns = DocUtils.GetNamespace (type);
4819 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
4820 buf.Append (ns).Append ('.');
4824 protected virtual string GetCSharpType (string t)
4827 case "System.Byte": return "byte";
4828 case "System.SByte": return "sbyte";
4829 case "System.Int16": return "short";
4830 case "System.Int32": return "int";
4831 case "System.Int64": return "long";
4833 case "System.UInt16": return "ushort";
4834 case "System.UInt32": return "uint";
4835 case "System.UInt64": return "ulong";
4837 case "System.Single": return "float";
4838 case "System.Double": return "double";
4839 case "System.Decimal": return "decimal";
4840 case "System.Boolean": return "bool";
4841 case "System.Char": return "char";
4842 case "System.Void": return "void";
4843 case "System.String": return "string";
4844 case "System.Object": return "object";
4849 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4851 if (context != null && context.TransformFlags != null &&
4852 (context.TransformFlags.Count == 0 || context.TransformFlags [context.TransformIndex])) {
4853 context.TransformIndex++;
4854 return buf.Append ("dynamic");
4857 if (type is GenericParameter)
4858 return AppendGenericParameterConstraints (buf, (GenericParameter) type, context).Append (type.Name);
4859 string t = type.FullName;
4860 if (!t.StartsWith ("System.")) {
4861 return base.AppendTypeName (buf, type, context);
4864 string s = GetCSharpType (t);
4866 if (context != null)
4867 context.TransformIndex++;
4868 return buf.Append (s);
4871 return base.AppendTypeName (buf, type, context);
4874 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
4876 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
4878 GenericParameterAttributes attrs = type.Attributes;
4879 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
4880 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
4884 buf.Append ("out ");
4888 protected override string GetTypeDeclaration (TypeDefinition type)
4890 string visibility = GetTypeVisibility (type.Attributes);
4891 if (visibility == null)
4894 StringBuilder buf = new StringBuilder ();
4896 buf.Append (visibility);
4899 MemberFormatter full = new CSharpFullMemberFormatter ();
4901 if (DocUtils.IsDelegate (type)) {
4902 buf.Append("delegate ");
4903 MethodDefinition invoke = type.GetMethod ("Invoke");
4904 buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
4905 buf.Append (GetName (type));
4906 AppendParameters (buf, invoke, invoke.Parameters);
4907 AppendGenericTypeConstraints (buf, type);
4910 return buf.ToString();
4913 if (type.IsAbstract && !type.IsInterface)
4914 buf.Append("abstract ");
4915 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
4916 buf.Append("sealed ");
4917 buf.Replace ("abstract sealed", "static");
4919 buf.Append (GetTypeKind (type));
4921 buf.Append (GetCSharpType (type.FullName) == null
4926 TypeReference basetype = type.BaseType;
4927 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
4930 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
4931 .Select (iface => full.GetName (iface))
4935 if (basetype != null || interface_names.Count > 0)
4938 if (basetype != null) {
4939 buf.Append (full.GetName (basetype));
4940 if (interface_names.Count > 0)
4944 for (int i = 0; i < interface_names.Count; i++){
4947 buf.Append (interface_names [i]);
4949 AppendGenericTypeConstraints (buf, type);
4952 return buf.ToString ();
4955 static string GetTypeKind (TypeDefinition t)
4961 if (t.IsClass || t.FullName == "System.Enum")
4965 throw new ArgumentException(t.FullName);
4968 static string GetTypeVisibility (TypeAttributes ta)
4970 switch (ta & TypeAttributes.VisibilityMask) {
4971 case TypeAttributes.Public:
4972 case TypeAttributes.NestedPublic:
4975 case TypeAttributes.NestedFamily:
4976 case TypeAttributes.NestedFamORAssem:
4984 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4986 if (type.GenericParameters.Count == 0)
4988 return AppendConstraints (buf, type.GenericParameters);
4991 private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
4993 foreach (GenericParameter genArg in genArgs) {
4994 GenericParameterAttributes attrs = genArg.Attributes;
4995 IList<TypeReference> constraints = genArg.Constraints;
4996 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
4999 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
5000 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
5001 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
5004 if (!isref && !isvt && !isnew && constraints.Count == 0)
5006 buf.Append (" where ").Append (genArg.Name).Append (" : ");
5008 buf.Append ("class");
5012 buf.Append ("struct");
5015 if (constraints.Count > 0 && !isvt) {
5018 buf.Append (GetTypeName (constraints [0]));
5019 for (int i = 1; i < constraints.Count; ++i)
5020 buf.Append (", ").Append (GetTypeName (constraints [i]));
5022 if (isnew && !isvt) {
5025 buf.Append ("new()");
5031 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5033 StringBuilder buf = new StringBuilder ();
5034 AppendVisibility (buf, constructor);
5035 if (buf.Length == 0)
5039 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
5040 AppendParameters (buf, constructor, constructor.Parameters);
5043 return buf.ToString ();
5046 protected override string GetMethodDeclaration (MethodDefinition method)
5048 string decl = base.GetMethodDeclaration (method);
5054 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
5056 if (DocUtils.IsExplicitlyImplemented (method)) {
5057 TypeReference iface;
5058 MethodReference ifaceMethod;
5059 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5060 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
5062 .Append (ifaceMethod.Name);
5064 return base.AppendMethodName (buf, method);
5067 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
5069 if (method.GenericParameters.Count == 0)
5071 return AppendConstraints (buf, method.GenericParameters);
5074 protected override string RefTypeModifier {
5078 protected override string GetFinalizerName (MethodDefinition method)
5080 return "~" + method.DeclaringType.Name + " ()";
5083 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
5087 if (method.IsPublic)
5088 return buf.Append ("public");
5089 if (method.IsFamily || method.IsFamilyOrAssembly)
5090 return buf.Append ("protected");
5094 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
5096 string modifiers = String.Empty;
5097 if (method.IsStatic) modifiers += " static";
5098 if (method.IsVirtual && !method.IsAbstract) {
5099 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
5100 else modifiers += " override";
5102 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
5103 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
5104 if (method.IsFinal) modifiers += " sealed";
5105 if (modifiers == " virtual sealed") modifiers = "";
5107 return buf.Append (modifiers);
5110 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
5112 if (method.IsGenericMethod ()) {
5113 IList<GenericParameter> args = method.GenericParameters;
5114 if (args.Count > 0) {
5116 buf.Append (args [0].Name);
5117 for (int i = 1; i < args.Count; ++i)
5118 buf.Append (",").Append (args [i].Name);
5125 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
5127 return AppendParameters (buf, method, parameters, '(', ')');
5130 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
5134 if (parameters.Count > 0) {
5135 if (DocUtils.IsExtensionMethod (method))
5136 buf.Append ("this ");
5137 AppendParameter (buf, parameters [0]);
5138 for (int i = 1; i < parameters.Count; ++i) {
5140 AppendParameter (buf, parameters [i]);
5144 return buf.Append (end);
5147 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
5149 if (parameter.ParameterType is ByReferenceType) {
5150 if (parameter.IsOut)
5151 buf.Append ("out ");
5153 buf.Append ("ref ");
5155 buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
5156 buf.Append (parameter.Name);
5157 if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant) {
5158 buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
5163 protected override string GetPropertyDeclaration (PropertyDefinition property)
5165 MethodDefinition method;
5167 string get_visible = null;
5168 if ((method = property.GetMethod) != null &&
5169 (DocUtils.IsExplicitlyImplemented (method) ||
5170 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5171 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5172 string set_visible = null;
5173 if ((method = property.SetMethod) != null &&
5174 (DocUtils.IsExplicitlyImplemented (method) ||
5175 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5176 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5178 if ((set_visible == null) && (get_visible == null))
5182 StringBuilder buf = new StringBuilder ();
5183 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
5184 buf.Append (visibility = get_visible);
5185 else if (set_visible != null && get_visible == null)
5186 buf.Append (visibility = set_visible);
5188 buf.Append (visibility = "public");
5190 // Pick an accessor to use for static/virtual/override/etc. checks.
5191 method = property.SetMethod;
5193 method = property.GetMethod;
5195 string modifiers = String.Empty;
5196 if (method.IsStatic) modifiers += " static";
5197 if (method.IsVirtual && !method.IsAbstract) {
5198 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
5199 modifiers += " virtual";
5201 modifiers += " override";
5203 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
5204 if (method.IsAbstract && !declDef.IsInterface)
5205 modifiers += " abstract";
5207 modifiers += " sealed";
5208 if (modifiers == " virtual sealed")
5210 buf.Append (modifiers).Append (' ');
5212 buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
5214 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
5215 string name = property.Name;
5216 foreach (MemberReference mi in defs) {
5217 if (mi == property) {
5222 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
5224 if (property.Parameters.Count != 0) {
5225 AppendParameters (buf, method, property.Parameters, '[', ']');
5229 if (get_visible != null) {
5230 if (get_visible != visibility)
5231 buf.Append (' ').Append (get_visible);
5232 buf.Append (" get;");
5234 if (set_visible != null) {
5235 if (set_visible != visibility)
5236 buf.Append (' ').Append (set_visible);
5237 buf.Append (" set;");
5241 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
5244 protected override string GetFieldDeclaration (FieldDefinition field)
5246 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
5247 if (declType.IsEnum && field.Name == "value__")
5248 return null; // This member of enums aren't documented.
5250 StringBuilder buf = new StringBuilder ();
5251 AppendFieldVisibility (buf, field);
5252 if (buf.Length == 0)
5255 if (declType.IsEnum)
5258 if (field.IsStatic && !field.IsLiteral)
5259 buf.Append (" static");
5260 if (field.IsInitOnly)
5261 buf.Append (" readonly");
5262 if (field.IsLiteral)
5263 buf.Append (" const");
5265 buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
5266 buf.Append (field.Name);
5267 AppendFieldValue (buf, field);
5270 return buf.ToString ();
5273 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
5276 return buf.Append ("public");
5277 if (field.IsFamily || field.IsFamilyOrAssembly)
5278 return buf.Append ("protected");
5282 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
5284 // enums have a value__ field, which we ignore
5285 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
5286 field.DeclaringType.IsGenericType ())
5288 if (field.HasConstant && field.IsLiteral) {
5291 val = field.Constant;
5296 buf.Append (" = ").Append ("null");
5297 else if (val is Enum)
5298 buf.Append (" = ").Append (val.ToString ());
5299 else if (val is IFormattable) {
5300 string value = ((IFormattable)val).ToString();
5302 value = "\"" + value + "\"";
5303 buf.Append (" = ").Append (value);
5309 protected override string GetEventDeclaration (EventDefinition e)
5311 StringBuilder buf = new StringBuilder ();
5312 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
5316 AppendModifiers (buf, e.AddMethod);
5318 buf.Append (" event ");
5319 buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters [0]))).Append (' ');
5320 buf.Append (e.Name).Append (';');
5322 return buf.ToString ();
5326 class CSharpMemberFormatter : CSharpFullMemberFormatter {
5327 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5333 class DocTypeFullMemberFormatter : MemberFormatter {
5334 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
5336 protected override char NestedTypeSeparator {
5341 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
5342 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5348 class SlashDocMemberFormatter : MemberFormatter {
5350 protected override char[] GenericTypeContainer {
5351 get {return new char[]{'{', '}'};}
5354 private bool AddTypeCount = true;
5356 private TypeReference genDeclType;
5357 private MethodReference genDeclMethod;
5359 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
5361 if (type is GenericParameter) {
5363 if (genDeclType != null) {
5364 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
5365 for (int i = 0; i < genArgs.Count; ++i) {
5366 if (genArgs [i].Name == type.Name) {
5367 buf.Append ('`').Append (i);
5372 if (genDeclMethod != null) {
5373 IList<GenericParameter> genArgs = null;
5374 if (genDeclMethod.IsGenericMethod ()) {
5375 genArgs = genDeclMethod.GenericParameters;
5376 for (int i = 0; i < genArgs.Count; ++i) {
5377 if (genArgs [i].Name == type.Name) {
5378 buf.Append ("``").Append (i);
5384 if (genDeclType == null && genDeclMethod == null) {
5385 // Probably from within an explicitly implemented interface member,
5386 // where CSC uses parameter names instead of indices (why?), e.g.
5387 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
5388 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
5389 buf.Append (type.Name);
5391 if (buf.Length == l) {
5392 throw new Exception (string.Format (
5393 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
5394 type.Name, genDeclType, genDeclMethod));
5398 base.AppendTypeName (buf, type, context);
5400 int numArgs = type.GenericParameters.Count;
5401 if (type.DeclaringType != null)
5402 numArgs -= type.GenericParameters.Count;
5404 buf.Append ('`').Append (numArgs);
5411 protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
5413 buf.Append (ArrayDelimeters [0]);
5414 int rank = array.Rank;
5417 for (int i = 1; i < rank; ++i) {
5421 return buf.Append (ArrayDelimeters [1]);
5424 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5427 base.AppendGenericType (buf, type, context);
5429 AppendType (buf, type, context);
5433 private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5435 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
5436 bool insertNested = false;
5437 int prevParamCount = 0;
5438 foreach (var decl in decls) {
5440 buf.Append (NestedTypeSeparator);
5441 insertNested = true;
5442 base.AppendTypeName (buf, decl, context);
5443 int argCount = DocUtils.GetGenericArgumentCount (decl);
5444 int numArgs = argCount - prevParamCount;
5445 prevParamCount = argCount;
5447 buf.Append ('`').Append (numArgs);
5452 public override string GetDeclaration (MemberReference member)
5454 TypeReference r = member as TypeReference;
5456 return "T:" + GetTypeName (r);
5458 return base.GetDeclaration (member);
5461 protected override string GetConstructorName (MethodReference constructor)
5463 return GetMethodDefinitionName (constructor, "#ctor");
5466 protected override string GetMethodName (MethodReference method)
5469 MethodDefinition methodDef = method as MethodDefinition;
5470 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
5473 TypeReference iface;
5474 MethodReference ifaceMethod;
5475 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
5476 AddTypeCount = false;
5477 name = GetTypeName (iface) + "." + ifaceMethod.Name;
5478 AddTypeCount = true;
5480 return GetMethodDefinitionName (method, name);
5483 private string GetMethodDefinitionName (MethodReference method, string name)
5485 StringBuilder buf = new StringBuilder ();
5486 buf.Append (GetTypeName (method.DeclaringType));
5488 buf.Append (name.Replace (".", "#"));
5489 if (method.IsGenericMethod ()) {
5490 IList<GenericParameter> genArgs = method.GenericParameters;
5491 if (genArgs.Count > 0)
5492 buf.Append ("``").Append (genArgs.Count);
5494 IList<ParameterDefinition> parameters = method.Parameters;
5496 genDeclType = method.DeclaringType;
5497 genDeclMethod = method;
5498 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
5502 genDeclMethod = null;
5504 return buf.ToString ();
5507 private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
5509 if (parameters.Count == 0)
5514 AppendParameter (buf, genArgs, parameters [0]);
5515 for (int i = 1; i < parameters.Count; ++i) {
5517 AppendParameter (buf, genArgs, parameters [i]);
5520 return buf.Append (')');
5523 private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
5525 AddTypeCount = false;
5526 buf.Append (GetTypeName (parameter.ParameterType));
5527 AddTypeCount = true;
5531 protected override string GetPropertyName (PropertyReference property)
5535 PropertyDefinition propertyDef = property as PropertyDefinition;
5536 MethodDefinition method = null;
5537 if (propertyDef != null)
5538 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
5539 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
5540 name = property.Name;
5542 TypeReference iface;
5543 MethodReference ifaceMethod;
5544 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5545 AddTypeCount = false;
5546 name = string.Join ("#", new string[]{
5547 GetTypeName (iface).Replace (".", "#"),
5548 DocUtils.GetMember (property.Name)
5550 AddTypeCount = true;
5553 StringBuilder buf = new StringBuilder ();
5554 buf.Append (GetName (property.DeclaringType));
5557 IList<ParameterDefinition> parameters = property.Parameters;
5558 if (parameters.Count > 0) {
5559 genDeclType = property.DeclaringType;
5561 IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
5562 AppendParameter (buf, genArgs, parameters [0]);
5563 for (int i = 1; i < parameters.Count; ++i) {
5565 AppendParameter (buf, genArgs, parameters [i]);
5570 return buf.ToString ();
5573 protected override string GetFieldName (FieldReference field)
5575 return string.Format ("{0}.{1}",
5576 GetName (field.DeclaringType), field.Name);
5579 protected override string GetEventName (EventReference e)
5581 return string.Format ("{0}.{1}",
5582 GetName (e.DeclaringType), e.Name);
5585 protected override string GetTypeDeclaration (TypeDefinition type)
5587 string name = GetName (type);
5593 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5595 string name = GetName (constructor);
5601 protected override string GetMethodDeclaration (MethodDefinition method)
5603 string name = GetName (method);
5606 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
5607 genDeclType = method.DeclaringType;
5608 genDeclMethod = method;
5609 name += "~" + GetName (method.ReturnType);
5611 genDeclMethod = null;
5616 protected override string GetPropertyDeclaration (PropertyDefinition property)
5618 string name = GetName (property);
5624 protected override string GetFieldDeclaration (FieldDefinition field)
5626 string name = GetName (field);
5632 protected override string GetEventDeclaration (EventDefinition e)
5634 string name = GetName (e);
5641 class FileNameMemberFormatter : SlashDocMemberFormatter {
5642 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5647 protected override char NestedTypeSeparator {
5652 class ResolvedTypeInfo {
5653 TypeDefinition typeDef;
5655 public ResolvedTypeInfo (TypeReference value) {
5659 public TypeReference Reference { get; private set; }
5661 public TypeDefinition Definition {
5663 if (typeDef == null) {
5664 typeDef = Reference.Resolve ();
5671 /// <summary>Formats attribute values. Should return true if it is able to format the value.</summary>
5672 class AttributeValueFormatter {
5673 public virtual bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5675 TypeReference valueType = type.Reference;
5677 returnvalue = "null";
5680 if (valueType.FullName == "System.Type") {
5681 var vTypeRef = v as TypeReference;
5682 if (vTypeRef != null)
5683 returnvalue = "typeof(" + NativeTypeManager.GetTranslatedName (vTypeRef) + ")"; // TODO: drop NS handling
5685 returnvalue = "typeof(" + v.ToString () + ")";
5689 if (valueType.FullName == "System.String") {
5690 returnvalue = "\"" + v.ToString () + "\"";
5693 if (valueType.FullName == "System.Char") {
5694 returnvalue = "'" + v.ToString () + "'";
5698 returnvalue = (bool)v ? "true" : "false";
5702 TypeDefinition valueDef = type.Definition;
5703 if (valueDef == null || !valueDef.IsEnum) {
5704 returnvalue = v.ToString ();
5708 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5709 var values = MDocUpdater.GetEnumerationValues (valueDef);
5710 long c = MDocUpdater.ToInt64 (v);
5711 if (values.ContainsKey (c)) {
5712 returnvalue = typename + "." + values [c];
5721 /// <summary>The final value formatter in the pipeline ... if no other formatter formats the value,
5722 /// then this one will serve as the default implementation.</summary>
5723 class DefaultAttributeValueFormatter : AttributeValueFormatter {
5724 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5726 returnvalue = "(" + MDocUpdater.GetDocTypeFullName (type.Reference) + ") " + v.ToString ();
5731 /// <summary>Flags enum formatter that assumes powers of two values.</summary>
5732 /// <remarks>As described here: https://msdn.microsoft.com/en-us/library/vstudio/ms229062(v=vs.100).aspx</remarks>
5733 class StandardFlagsEnumFormatter : AttributeValueFormatter {
5734 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5736 TypeReference valueType = type.Reference;
5737 TypeDefinition valueDef = type.Definition;
5738 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
5740 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5741 var values = MDocUpdater.GetEnumerationValues (valueDef);
5742 long c = MDocUpdater.ToInt64 (v);
5743 returnvalue = string.Join (" | ",
5744 (from i in values.Keys
5745 where (c & i) == i && i != 0
5746 select typename + "." + values [i])
5747 .DefaultIfEmpty (c.ToString ()).ToArray ());
5757 /// <summary>A custom formatter for the ObjCRuntime.Platform enumeration.</summary>
5758 class ApplePlatformEnumFormatter : AttributeValueFormatter {
5759 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5761 TypeReference valueType = type.Reference;
5762 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5763 TypeDefinition valueDef = type.Definition;
5764 if (typename.Contains ("ObjCRuntime.Platform") && valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
5766 var values = MDocUpdater.GetEnumerationValues (valueDef);
5767 long c = MDocUpdater.ToInt64 (v);
5769 returnvalue = Format (c, values, typename);
5777 string Format (long c, IDictionary<long, string> values, string typename)
5779 int iosarch, iosmajor, iosminor, iossubminor;
5780 int macarch, macmajor, macminor, macsubminor;
5781 GetEncodingiOS (c, out iosarch, out iosmajor, out iosminor, out iossubminor);
5782 GetEncodingMac ((ulong)c, out macarch, out macmajor, out macminor, out macsubminor);
5784 if (iosmajor == 0 & iosminor == 0 && iossubminor == 0) {
5785 return FormatValues ("Mac", macarch, macmajor, macminor, macsubminor);
5788 if (macmajor == 0 & macminor == 0 && macsubminor == 0) {
5789 return FormatValues ("iOS", iosarch, iosmajor, iosminor, iossubminor);
5792 return string.Format ("(Platform){0}", c);
5795 string FormatValues (string plat, int arch, int major, int minor, int subminor)
5797 string archstring = "";
5806 return string.Format ("Platform.{4}_{0}_{1}{2} | Platform.{4}_Arch{3}",
5809 subminor == 0 ? "" : "_" + subminor.ToString (),
5815 void GetEncodingiOS (long entireLong, out int archindex, out int major, out int minor, out int subminor)
5817 long lowerBits = entireLong & 0xffffffff;
5818 int lowerBitsAsInt = (int) lowerBits;
5819 GetEncoding (lowerBitsAsInt, out archindex, out major, out minor, out subminor);
5822 void GetEncodingMac (ulong entireLong, out int archindex, out int major, out int minor, out int subminor)
5824 ulong higherBits = entireLong & 0xffffffff00000000;
5825 int higherBitsAsInt = (int) ((higherBits) >> 32);
5826 GetEncoding (higherBitsAsInt, out archindex, out major, out minor, out subminor);
5829 void GetEncoding (Int32 encodedBits, out int archindex, out int major, out int minor, out int subminor)
5831 // format is AAJJNNSS
5832 archindex = (int)((encodedBits & 0xFF000000) >> 24);
5833 major = (int)((encodedBits & 0x00FF0000) >> 16);
5834 minor = (int)((encodedBits & 0x0000FF00) >> 8);
5835 subminor = (int)((encodedBits & 0x000000FF) >> 0);