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);
190 public static bool HasDroppedAnyNamespace ()
192 return !string.IsNullOrWhiteSpace (droppedNamespace);
196 static List<string> droppedAssemblies = new List<string>();
198 public string PreserveTag { get; set; }
199 public static MDocUpdater Instance { get; private set; }
200 public static bool SwitchingToMagicTypes { get; private set; }
202 public override void Run (IEnumerable<string> args)
205 show_exceptions = DebugOutput;
206 var types = new List<string> ();
207 var p = new OptionSet () {
209 "Delete removed members from the XML files.",
210 v => delete = v != null },
212 "Document potential exceptions that members can generate. {SOURCES} " +
213 "is a comma-separated list of:\n" +
214 " asm Method calls in same assembly\n" +
215 " depasm Method calls in dependent assemblies\n" +
216 " all Record all possible exceptions\n" +
217 " added Modifier; only create <exception/>s\n" +
218 " for NEW types/members\n" +
219 "If nothing is specified, then only exceptions from the member will " +
221 v => exceptions = ParseExceptionLocations (v) },
223 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
226 case "ignore-missing-types":
227 ignore_missing_types = true;
229 case "no-assembly-versions":
230 no_assembly_versions = true;
233 throw new Exception ("Unsupported flag `" + v + "'.");
236 { "fignore-missing-types",
237 "Do not report an error if a --type=TYPE type\nwas not found.",
238 v => ignore_missing_types = v != null },
239 { "fno-assembly-versions",
240 "Do not generate //AssemblyVersion elements.",
241 v => no_assembly_versions = v != null },
243 "Import documentation from {FILE}.",
244 v => AddImporter (v) },
246 "Check for assembly references in {DIRECTORY}.",
247 v => assemblyResolver.AddSearchDirectory (v) },
249 "Ignored for compatibility with update-ecma-xml.",
252 "Root {DIRECTORY} to generate/update documentation.",
255 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
256 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
257 v => assemblyResolver.AddSearchDirectory (Path.GetDirectoryName (v)) },
259 "Manually specify the assembly {VERSION} that new members were added in.",
262 "Only update documentation for {TYPE}.",
263 v => types.Add (v) },
265 "When processing assembly {ASSEMBLY}, strip off leading namespace {PREFIX}:\n" +
266 " e.g. --dropns ASSEMBLY=PREFIX",
268 var parts = v.Split ('=');
269 if (parts.Length != 2) { Console.Error.WriteLine ("Invalid dropns input"); return; }
270 var assembly = Path.GetFileName (parts [0].Trim ());
271 var prefix = parts [1].Trim();
272 droppedAssemblies.Add (assembly);
273 droppedNamespace = prefix;
276 "If the new assembly is switching to 'magic types', then this switch should be defined.",
277 v => SwitchingToMagicTypes = true },
279 "Do not delete members that don't exist in the assembly, but rather mark them as preserved.",
280 v => PreserveTag = "true" },
282 var assemblies = Parse (p, args, "update",
283 "[OPTIONS]+ ASSEMBLIES",
284 "Create or update documentation from ASSEMBLIES.");
285 if (assemblies == null)
287 if (assemblies.Count == 0)
288 Error ("No assemblies specified.");
290 foreach (var dir in assemblies
291 .Where (a => a.Contains (Path.DirectorySeparatorChar))
292 .Select (a => Path.GetDirectoryName (a)))
293 assemblyResolver.AddSearchDirectory (dir);
295 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
298 throw new InvalidOperationException("The --out option is required.");
300 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
302 // Store types that have been forwarded to avoid duplicate generation
303 GatherForwardedTypes ();
305 docEnum = docEnum ?? new DocumentationEnumerator ();
307 // PERFORM THE UPDATES
309 if (types.Count > 0) {
311 DoUpdateTypes (srcPath, types, srcPath);
314 else if (opts.@namespace != null)
315 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
316 Path.Combine (dest_dir, opts.@namespace));
319 DoUpdateAssemblies (srcPath, srcPath);
321 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
323 public static bool IsInAssemblies(string name) {
324 var query = Instance.assemblies.Where (a => a.MainModule.Name == name).ToArray ();
325 return query.Length > 0;
327 void AddImporter (string path)
330 XmlReader r = new XmlTextReader (path);
332 while (r.NodeType != XmlNodeType.Element) {
334 Error ("Unable to read XML file: {0}.", path);
336 if (r.LocalName == "doc") {
337 importers.Add (new MsxdocDocumentationImporter (path));
339 else if (r.LocalName == "Libraries") {
340 var ecmadocs = new XmlTextReader (path);
341 docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
342 importers.Add (new EcmaDocumentationImporter (ecmadocs));
345 Error ("Unsupported XML format within {0}.", path);
348 } catch (Exception e) {
349 Environment.ExitCode = 1;
350 Error ("Could not load XML file: {0}.", e.Message);
354 void GatherForwardedTypes ()
356 foreach (var asm in assemblies)
357 foreach (var type in asm.MainModule.ExportedTypes.Where (t => t.IsForwarder).Select (t => t.FullName))
358 forwardedTypes.Add (type);
361 static ExceptionLocations ParseExceptionLocations (string s)
363 ExceptionLocations loc = ExceptionLocations.Member;
366 foreach (var type in s.Split (',')) {
368 case "added": loc |= ExceptionLocations.AddedMembers; break;
369 case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
370 case "asm": loc |= ExceptionLocations.Assembly; break;
371 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
372 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
378 internal void Warning (string format, params object[] args)
380 Message (TraceLevel.Warning, "mdoc: " + format, args);
383 private AssemblyDefinition LoadAssembly (string name)
385 AssemblyDefinition assembly = null;
387 assembly = AssemblyDefinition.ReadAssembly (name, new ReaderParameters { AssemblyResolver = assemblyResolver });
388 } catch (System.IO.FileNotFoundException) { }
390 if (assembly == null)
391 throw new InvalidOperationException("Assembly " + name + " not found.");
396 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
397 OrderTypeAttributes (element);
398 XmlTextWriter writer = new XmlTextWriter(output);
399 writer.Formatting = Formatting.Indented;
400 writer.Indentation = 2;
401 writer.IndentChar = ' ';
402 element.WriteTo(writer);
406 private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
408 Action<string> creator = file => {
409 using (var writer = OpenWrite (file, mode))
413 MdocFile.UpdateFile (filename, creator);
416 private static void OrderTypeAttributes (XmlElement e)
418 foreach (XmlElement type in e.SelectNodes ("//Type")) {
419 OrderTypeAttributes (type.Attributes);
423 static readonly string[] TypeAttributeOrder = {
424 "Name", "FullName", "FullNameSP", "Maintainer"
427 private static void OrderTypeAttributes (XmlAttributeCollection c)
429 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
430 for (int i = 0; i < c.Count; ++i) {
431 XmlAttribute a = c [i];
432 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
433 if (a.Name == TypeAttributeOrder [j]) {
439 for (int i = attrs.Length-1; i >= 0; --i) {
440 XmlAttribute n = attrs [i];
443 XmlAttribute r = null;
444 for (int j = i+1; j < attrs.Length; ++j) {
445 if (attrs [j] != null) {
452 if (c [n.Name] != null) {
453 c.RemoveNamedItem (n.Name);
454 c.InsertBefore (n, r);
459 private XmlDocument CreateIndexStub()
461 XmlDocument index = new XmlDocument();
463 XmlElement index_root = index.CreateElement("Overview");
464 index.AppendChild(index_root);
466 if (assemblies.Count == 0)
467 throw new Exception ("No assembly");
469 XmlElement index_assemblies = index.CreateElement("Assemblies");
470 index_root.AppendChild(index_assemblies);
472 XmlElement index_remarks = index.CreateElement("Remarks");
473 index_remarks.InnerText = "To be added.";
474 index_root.AppendChild(index_remarks);
476 XmlElement index_copyright = index.CreateElement("Copyright");
477 index_copyright.InnerText = "To be added.";
478 index_root.AppendChild(index_copyright);
480 XmlElement index_types = index.CreateElement("Types");
481 index_root.AppendChild(index_types);
486 private static void WriteNamespaceStub(string ns, string outdir) {
487 XmlDocument index = new XmlDocument();
489 XmlElement index_root = index.CreateElement("Namespace");
490 index.AppendChild(index_root);
492 index_root.SetAttribute("Name", ns);
494 XmlElement index_docs = index.CreateElement("Docs");
495 index_root.AppendChild(index_docs);
497 XmlElement index_summary = index.CreateElement("summary");
498 index_summary.InnerText = "To be added.";
499 index_docs.AppendChild(index_summary);
501 XmlElement index_remarks = index.CreateElement("remarks");
502 index_remarks.InnerText = "To be added.";
503 index_docs.AppendChild(index_remarks);
505 WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
506 writer => WriteXml (index.DocumentElement, writer));
509 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
511 var index = CreateIndexForTypes (dest);
513 var found = new HashSet<string> ();
514 foreach (AssemblyDefinition assembly in assemblies) {
515 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames)) {
516 string relpath = DoUpdateType (type, basepath, dest);
520 found.Add (type.FullName);
525 index.Add (assembly);
533 if (ignore_missing_types)
536 var notFound = from n in typenames where !found.Contains (n) select n;
538 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
541 class IndexForTypes {
547 XmlElement index_types;
548 XmlElement index_assemblies;
550 public IndexForTypes (MDocUpdater app, string indexFile, XmlDocument index)
553 this.indexFile = indexFile;
556 index_types = WriteElement (index.DocumentElement, "Types");
557 index_assemblies = WriteElement (index.DocumentElement, "Assemblies");
560 public void Add (AssemblyDefinition assembly)
562 if (index_assemblies.SelectSingleNode ("Assembly[@Name='" + assembly.Name.Name + "']") != null)
565 app.AddIndexAssembly (assembly, index_assemblies);
568 public void Add (TypeDefinition type)
570 app.AddIndexType (type, index_types);
575 SortIndexEntries (index_types);
576 WriteFile (indexFile, FileMode.Create,
577 writer => WriteXml (index.DocumentElement, writer));
581 IndexForTypes CreateIndexForTypes (string dest)
583 string indexFile = Path.Combine (dest, "index.xml");
584 if (File.Exists (indexFile))
586 return new IndexForTypes (this, indexFile, CreateIndexStub ());
589 /// <summary>Constructs the presumed path to the type's documentation file</summary>
590 /// <returns><c>true</c>, if the type file was found, <c>false</c> otherwise.</returns>
591 /// <param name="result">A typle that contains 1) the 'reltypefile', 2) the 'typefile', and 3) the file info</param>
592 bool TryFindTypeFile(string nsname, string typename, string basepath, out Tuple<string, string, FileInfo> result) {
593 string reltypefile = DocUtils.PathCombine (nsname, typename + ".xml");
594 string typefile = Path.Combine (basepath, reltypefile);
595 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
597 result = new Tuple<string, string, FileInfo> (reltypefile, typefile, file);
602 public string DoUpdateType (TypeDefinition type, string basepath, string dest)
604 if (type.Namespace == null)
605 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
607 if (!IsPublic (type))
610 // Must get the A+B form of the type name.
611 string typename = GetTypeFileName(type);
612 string nsname = DocUtils.GetNamespace (type);
614 // Find the file, if it exists
615 string[] searchLocations = new string[] {
619 if (MDocUpdater.HasDroppedNamespace (type)) {
620 // If dropping namespace, types may have moved into a couple of different places.
621 var newSearchLocations = searchLocations.Union (new string[] {
622 string.Format ("{0}.{1}", droppedNamespace, nsname),
623 nsname.Replace (droppedNamespace + ".", string.Empty),
624 MDocUpdater.droppedNamespace
627 searchLocations = newSearchLocations.ToArray ();
630 string reltypefile="", typefile="";
631 System.IO.FileInfo file = null;
633 foreach (var f in searchLocations) {
634 Tuple<string, string, FileInfo> result;
635 bool fileExists = TryFindTypeFile (f, typename, basepath, out result);
638 reltypefile = result.Item1;
639 typefile = result.Item2;
646 if (file == null || !file.Exists) {
647 // we were not able to find a file, let's use the original type informatio.
648 // so that we create the stub in the right place.
649 Tuple<string, string, FileInfo> result;
650 TryFindTypeFile (nsname, typename, basepath, out result);
652 reltypefile = result.Item1;
653 typefile = result.Item2;
657 string output = null;
660 } else if (dest == "-") {
663 output = Path.Combine (dest, reltypefile);
666 if (file != null && file.Exists) {
668 XmlDocument basefile = new XmlDocument();
670 basefile.Load(typefile);
671 } catch (Exception e) {
672 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
675 DoUpdateType2("Updating", basefile, type, output, false);
678 XmlElement td = StubType(type, output);
685 public void DoUpdateNS (string ns, string nspath, string outpath)
687 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
688 AssemblyDefinition assembly = assemblies [0];
690 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
691 XmlDocument basefile = new XmlDocument();
692 string typefile = Path.Combine(nspath, file.Name);
694 basefile.Load(typefile);
695 } catch (Exception e) {
696 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
700 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
701 TypeDefinition type = assembly.GetType(typename);
704 if (!string.IsNullOrWhiteSpace (droppedNamespace)) {
705 string nameWithNs = string.Format ("{0}.{1}", droppedNamespace, typename);
706 type = assembly.GetType (nameWithNs);
708 Warning ("Type no longer in assembly: " + typename);
715 seenTypes[type] = seenTypes;
716 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false);
719 // Stub types not in the directory
720 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
721 if (type.Namespace != ns || seenTypes.ContainsKey(type))
724 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"));
725 if (td == null) continue;
729 private static string GetTypeFileName (TypeReference type)
731 return filenameFormatter.GetName (type);
734 public static string GetTypeFileName (string typename)
736 StringBuilder filename = new StringBuilder (typename.Length);
740 for (int i = 0; i < typename.Length; ++i) {
741 char c = typename [i];
750 filename.Append ('`').Append ((numArgs+1).ToString());
765 return filename.ToString ();
768 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
770 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
771 index_assembly.SetAttribute ("Name", assembly.Name.Name);
772 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
774 AssemblyNameDefinition name = assembly.Name;
775 if (name.HasPublicKey) {
776 XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
777 var key = new StringBuilder (name.PublicKey.Length*3 + 2);
779 foreach (byte b in name.PublicKey)
780 key.AppendFormat ("{0,2:x2} ", b);
782 pubkey.InnerText = key.ToString ();
783 index_assembly.AppendChild (pubkey);
786 if (!string.IsNullOrEmpty (name.Culture)) {
787 XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
788 culture.InnerText = name.Culture;
789 index_assembly.AppendChild (culture);
792 MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
793 parent.AppendChild(index_assembly);
796 private void AddIndexType (TypeDefinition type, XmlElement index_types)
798 string typename = GetTypeFileName(type);
800 // Add namespace and type nodes into the index file as needed
801 string ns = DocUtils.GetNamespace (type);
802 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode ("Namespace[@Name='" + ns + "']");
803 if (nsnode == null) {
804 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
805 nsnode.SetAttribute ("Name", ns);
806 index_types.AppendChild (nsnode);
808 string doc_typename = GetDocTypeName (type);
809 XmlElement typenode = (XmlElement) nsnode.SelectSingleNode ("Type[@Name='" + typename + "']");
810 if (typenode == null) {
811 typenode = index_types.OwnerDocument.CreateElement ("Type");
812 typenode.SetAttribute ("Name", typename);
813 nsnode.AppendChild (typenode);
815 if (typename != doc_typename)
816 typenode.SetAttribute("DisplayName", doc_typename);
818 typenode.RemoveAttribute("DisplayName");
820 typenode.SetAttribute ("Kind", GetTypeKind (type));
823 private void DoUpdateAssemblies (string source, string dest)
825 string indexfile = dest + "/index.xml";
827 if (System.IO.File.Exists(indexfile)) {
828 index = new XmlDocument();
829 index.Load(indexfile);
832 ClearElement(index.DocumentElement, "Assembly");
833 ClearElement(index.DocumentElement, "Attributes");
835 index = CreateIndexStub();
838 string defaultTitle = "Untitled";
839 if (assemblies.Count == 1)
840 defaultTitle = assemblies[0].Name.Name;
841 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
843 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
844 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
845 index_assemblies.RemoveAll ();
848 HashSet<string> goodfiles = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
850 foreach (AssemblyDefinition assm in assemblies) {
851 AddIndexAssembly (assm, index_assemblies);
852 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
855 SortIndexEntries (index_types);
857 CleanupFiles (dest, goodfiles);
858 CleanupIndexTypes (index_types, goodfiles);
859 CleanupExtensions (index_types);
861 WriteFile (indexfile, FileMode.Create,
862 writer => WriteXml(index.DocumentElement, writer));
865 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
867 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
869 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
870 string typename = GetTypeFileName(type);
871 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0 || forwardedTypes.Contains (type.FullName))
874 string reltypepath = DoUpdateType (type, source, dest);
875 if (reltypepath == null)
878 // Add namespace and type nodes into the index file as needed
879 AddIndexType (type, index_types);
881 // Ensure the namespace index file exists
882 string namespaceToUse = type.Namespace;
883 if (HasDroppedNamespace(assembly)) {
884 namespaceToUse = string.Format ("{0}.{1}", droppedNamespace, namespaceToUse);
886 string onsdoc = DocUtils.PathCombine (dest, namespaceToUse + ".xml");
887 string nsdoc = DocUtils.PathCombine (dest, "ns-" + namespaceToUse + ".xml");
888 if (File.Exists (onsdoc)) {
889 File.Move (onsdoc, nsdoc);
892 if (!File.Exists (nsdoc)) {
893 Console.WriteLine("New Namespace File: " + type.Namespace);
894 WriteNamespaceStub(namespaceToUse, dest);
897 goodfiles.Add (reltypepath);
901 private static void SortIndexEntries (XmlElement indexTypes)
903 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
904 XmlNodeComparer c = new AttributeNameComparer ();
905 SortXmlNodes (indexTypes, namespaces, c);
907 for (int i = 0; i < namespaces.Count; ++i)
908 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
911 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
913 MyXmlNodeList l = new MyXmlNodeList (children.Count);
914 for (int i = 0; i < children.Count; ++i)
915 l.Add (children [i]);
917 for (int i = l.Count - 1; i > 0; --i) {
918 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
922 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
924 public abstract int Compare (XmlNode x, XmlNode y);
926 public int Compare (object x, object y)
928 return Compare ((XmlNode) x, (XmlNode) y);
932 class AttributeNameComparer : XmlNodeComparer {
935 public AttributeNameComparer ()
940 public AttributeNameComparer (string attribute)
942 this.attribute = attribute;
945 public override int Compare (XmlNode x, XmlNode y)
947 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
951 class VersionComparer : XmlNodeComparer {
952 public override int Compare (XmlNode x, XmlNode y)
954 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
955 string a = GetVersion (x.InnerText);
956 string b = GetVersion (y.InnerText);
957 return new Version (a).CompareTo (new Version (b));
960 static string GetVersion (string v)
962 int n = v.IndexOf ("x");
965 return v.Substring (0, n-1);
969 private static string GetTypeKind (TypeDefinition type)
972 return "Enumeration";
973 if (type.IsValueType)
975 if (type.IsInterface)
977 if (DocUtils.IsDelegate (type))
979 if (type.IsClass || type.FullName == "System.Enum") // FIXME
981 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
984 public static bool IsPublic (TypeDefinition type)
986 TypeDefinition decl = type;
987 while (decl != null) {
988 if (!(decl.IsPublic || decl.IsNestedPublic ||
989 decl.IsNestedFamily || decl.IsNestedFamily || decl.IsNestedFamilyOrAssembly)) {
992 decl = (TypeDefinition) decl.DeclaringType;
997 private void CleanupFiles (string dest, HashSet<string> goodfiles)
999 // Look for files that no longer correspond to types
1000 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
1001 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
1002 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
1003 if (!goodfiles.Contains (relTypeFile)) {
1004 XmlDocument doc = new XmlDocument ();
1005 doc.Load (typefile.FullName);
1006 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
1007 string assemblyName = doc.SelectSingleNode ("/Type/AssemblyInfo/AssemblyName").InnerText;
1008 AssemblyDefinition assembly = assemblies.FirstOrDefault (a => a.Name.Name == assemblyName);
1010 Action saveDoc = () => {
1011 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
1012 WriteXml(doc.DocumentElement, writer);
1015 if (e != null && !no_assembly_versions && assembly != null && assemblyName != null && UpdateAssemblyVersions (e, assembly, GetAssemblyVersions(assemblyName), false)) {
1017 goodfiles.Add (relTypeFile);
1021 Action actuallyDelete = () => {
1022 string newname = typefile.FullName + ".remove";
1023 try { System.IO.File.Delete (newname); } catch (Exception) { Warning ("Unable to delete existing file: {0}", newname); }
1024 try { typefile.MoveTo (newname); } catch (Exception) { Warning ("Unable to rename to: {0}", newname); }
1025 Console.WriteLine ("Class no longer present; file renamed: " + Path.Combine (nsdir.Name, typefile.Name));
1028 if (string.IsNullOrWhiteSpace (PreserveTag)) { // only do this if there was not a -preserve
1031 var unifiedAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[@apistyle='unified']");
1032 var classicAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[@apistyle='classic']");
1033 var unifiedMembers = doc.SelectNodes ("//Member[@apistyle='unified']|//Member/AssemblyInfo[@apistyle='unified']");
1034 var classicMembers = doc.SelectNodes ("//Member[@apistyle='classic']|//Member/AssemblyInfo[@apistyle='classic']");
1035 bool isUnifiedRun = HasDroppedAnyNamespace ();
1036 bool isClassicOrNormalRun = !isUnifiedRun;
1038 Action<XmlNode, ApiStyle> removeStyles = (x, style) => {
1039 var styledNodes = doc.SelectNodes("//*[@apistyle='"+ style.ToString ().ToLowerInvariant () +"']");
1040 if (styledNodes != null && styledNodes.Count > 0) {
1041 foreach(var node in styledNodes.Cast<XmlNode> ()) {
1042 node.ParentNode.RemoveChild (node);
1047 if (isClassicOrNormalRun) {
1048 if (unifiedAssemblyNode != null || unifiedMembers.Count > 0) {
1049 Warning ("*** this type is marked as unified, not deleting during this run: {0}", typefile.FullName);
1050 // if truly removed from both assemblies, it will be removed fully during the unified run
1051 removeStyles (doc, ApiStyle.Classic);
1054 // we should be safe to delete here because it was not marked as a unified assembly
1059 if (classicAssemblyNode != null || classicMembers.Count > 0) {
1060 Warning ("*** this type is marked as classic, not deleting {0}", typefile.FullName);
1063 // safe to delete because it wasn't marked as a classic assembly, so the type is gone in both.
1073 private static TextWriter OpenWrite (string path, FileMode mode)
1075 var w = new StreamWriter (
1076 new FileStream (path, mode),
1077 new UTF8Encoding (false)
1083 private string[] GetAssemblyVersions (string assemblyName)
1085 return (from a in assemblies
1086 where a.Name.Name == assemblyName
1087 select GetAssemblyVersion (a)).ToArray ();
1090 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
1092 // Look for type nodes that no longer correspond to types
1093 MyXmlNodeList remove = new MyXmlNodeList ();
1094 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
1095 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
1096 if (!goodfiles.Contains (fulltypename)) {
1097 remove.Add (typenode);
1100 foreach (XmlNode n in remove)
1101 n.ParentNode.RemoveChild (n);
1104 private void CleanupExtensions (XmlElement index_types)
1106 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
1107 if (extensionMethods.Count == 0) {
1110 index_types.SelectSingleNode ("/Overview").RemoveChild (e);
1114 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
1115 index_types.SelectSingleNode ("/Overview").AppendChild (e);
1119 extensionMethods.Sort (DefaultExtensionMethodComparer);
1120 foreach (XmlNode m in extensionMethods) {
1121 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
1125 class ExtensionMethodComparer : XmlNodeComparer {
1126 public override int Compare (XmlNode x, XmlNode y)
1128 XmlNode xLink = x.SelectSingleNode ("Member/Link");
1129 XmlNode yLink = y.SelectSingleNode ("Member/Link");
1131 int n = xLink.Attributes ["Type"].Value.CompareTo (
1132 yLink.Attributes ["Type"].Value);
1135 n = xLink.Attributes ["Member"].Value.CompareTo (
1136 yLink.Attributes ["Member"].Value);
1137 if (n == 0 && !object.ReferenceEquals (x, y))
1138 throw new InvalidOperationException ("Duplicate extension method found!");
1143 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
1145 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince)
1147 Console.WriteLine(message + ": " + type.FullName);
1149 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
1151 // Update type metadata
1152 UpdateType(basefile.DocumentElement, type);
1154 // Update existing members. Delete member nodes that no longer should be there,
1155 // and remember what members are already documented so we don't add them again.
1157 MyXmlNodeList todelete = new MyXmlNodeList ();
1159 foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
1160 XmlElement oldmember = info.Node;
1161 MemberReference oldmember2 = info.Member;
1163 if (info.Member != null && info.Node != null) {
1164 // Check for an error condition where the xml MemberName doesn't match the matched member
1165 var memberName = GetMemberName (info.Member);
1166 var memberAttribute = info.Node.Attributes ["MemberName"];
1167 if (memberAttribute == null || (memberAttribute.Value != memberName && memberAttribute.Value.Split (',').Length != memberName.Split (',').Length)) {
1168 oldmember.SetAttribute ("MemberName", memberName);
1172 string sig = oldmember2 != null ? memberFormatters [0].GetDeclaration (oldmember2) : null;
1174 // Interface implementations and overrides are deleted from the docs
1175 // unless the overrides option is given.
1176 if (oldmember2 != null && sig == null)
1179 // Deleted (or signature changed)
1180 if (oldmember2 == null) {
1181 if (!no_assembly_versions && UpdateAssemblyVersions (oldmember, type.Module.Assembly, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
1184 DeleteMember ("Member Removed", output, oldmember, todelete, type);
1189 if (seenmembers.ContainsKey (sig)) {
1190 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
1191 // ignore, already seen
1193 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
1194 DeleteMember ("Duplicate Member Found", output, oldmember, todelete, type);
1196 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
1200 // Update signature information
1203 // get all apistyles of sig from info.Node
1204 var styles = oldmember.GetElementsByTagName ("MemberSignature").Cast<XmlElement> ()
1205 .Where (x => x.GetAttribute ("Language") == "C#" && !seenmembers.ContainsKey(x.GetAttribute("Value")))
1206 .Select (x => x.GetAttribute ("Value"));
1208 foreach (var stylesig in styles) {
1209 seenmembers.Add (stylesig, oldmember);
1212 foreach (XmlElement oldmember in todelete)
1213 oldmember.ParentNode.RemoveChild (oldmember);
1216 if (!DocUtils.IsDelegate (type)) {
1217 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
1218 var typemembers = type.GetMembers()
1220 if (m is TypeDefinition) return false;
1221 string sig = memberFormatters [0].GetDeclaration (m);
1222 if (sig == null) return false;
1223 if (seenmembers.ContainsKey(sig)) return false;
1225 // Verify that the member isn't an explicitly implemented
1226 // member of an internal interface, in which case we shouldn't return true.
1227 MethodDefinition methdef = null;
1228 if (m is MethodDefinition)
1229 methdef = m as MethodDefinition;
1230 else if (m is PropertyDefinition) {
1231 var prop = m as PropertyDefinition;
1232 methdef = prop.GetMethod ?? prop.SetMethod;
1235 if (methdef != null) {
1236 TypeReference iface;
1237 MethodReference imethod;
1239 if (methdef.Overrides.Count == 1) {
1240 DocUtils.GetInfoForExplicitlyImplementedMethod (methdef, out iface, out imethod);
1241 if (!IsPublic (iface.Resolve ())) return false;
1248 foreach (MemberReference m in typemembers) {
1249 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
1250 if (mm == null) continue;
1252 if (MDocUpdater.SwitchingToMagicTypes) {
1253 // this is a unified style API that obviously doesn't exist in the classic API. Let's mark
1254 // it with apistyle="unified", so that it's not displayed for classic style APIs
1255 mm.SetAttribute ("apistyle", "unified");
1258 members.AppendChild( mm );
1260 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
1265 // Import code snippets from files
1266 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
1267 if (!(code is XmlElement)) continue;
1268 string file = ((XmlElement)code).GetAttribute("src");
1269 string lang = ((XmlElement)code).GetAttribute("lang");
1271 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
1273 code.InnerText = src;
1277 if (insertSince && since != null) {
1278 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
1279 docs.AppendChild (CreateSinceNode (basefile));
1283 XmlElement d = basefile.DocumentElement ["Docs"];
1284 XmlElement m = basefile.DocumentElement ["Members"];
1285 if (d != null && m != null)
1286 basefile.DocumentElement.InsertBefore (
1287 basefile.DocumentElement.RemoveChild (d), m);
1288 SortTypeMembers (m);
1292 WriteXml(basefile.DocumentElement, Console.Out);
1294 FileInfo file = new FileInfo (output);
1295 if (!file.Directory.Exists) {
1296 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
1297 file.Directory.Create ();
1299 WriteFile (output, FileMode.Create,
1300 writer => WriteXml(basefile.DocumentElement, writer));
1304 private string GetCodeSource (string lang, string file)
1307 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
1308 // Grab the specified region
1309 string region = "#region " + file.Substring (anchorStart + 4);
1310 file = file.Substring (0, anchorStart + 3);
1312 using (StreamReader reader = new StreamReader (file)) {
1314 StringBuilder src = new StringBuilder ();
1316 while ((line = reader.ReadLine ()) != null) {
1317 if (line.Trim() == region) {
1318 indent = line.IndexOf (region);
1321 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
1326 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
1329 return src.ToString ();
1331 } catch (Exception e) {
1332 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
1333 file, region, show_exceptions ? e.ToString () : e.Message);
1338 using (StreamReader reader = new StreamReader (file))
1339 return reader.ReadToEnd ();
1340 } catch (Exception e) {
1341 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
1346 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete, TypeDefinition type)
1348 string format = output != null
1349 ? "{0}: File='{1}'; Signature='{4}'"
1350 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1351 string signature = member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value;
1355 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1356 member.Attributes ["MemberName"].Value,
1359 // Identify all of the different states that could affect our decision to delete the member
1360 bool shouldPreserve = !string.IsNullOrWhiteSpace (PreserveTag);
1361 bool hasContent = MemberDocsHaveUserContent (member);
1362 bool shouldDelete = !shouldPreserve && (delete || !hasContent);
1364 bool unifiedRun = HasDroppedNamespace (type);
1366 var classicAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[@apistyle='classic']");
1367 bool nodeIsClassic = classicAssemblyInfo != null || member.HasApiStyle (ApiStyle.Classic);
1368 var unifiedAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']");
1369 bool nodeIsUnified = unifiedAssemblyInfo != null || member.HasApiStyle (ApiStyle.Unified);
1371 Action actuallyDelete = () => {
1372 todelete.Add (member);
1376 if (!shouldDelete) {
1377 // explicitly not deleting
1378 string message = shouldPreserve ?
1379 "Not deleting '{0}' due to --preserve." :
1380 "Not deleting '{0}'; must be enabled with the --delete option";
1381 Warning (message, signature);
1382 } else if (unifiedRun && nodeIsClassic) {
1383 // this is a unified run, and the member doesn't exist, but is marked as being in the classic assembly.
1384 member.RemoveApiStyle (ApiStyle.Unified);
1385 Warning ("Not removing '{0}' since it's still in the classic assembly.", signature);
1386 } else if (unifiedRun && !nodeIsClassic) {
1387 // unified run, and the node is not classic, which means it doesn't exist anywhere.
1390 if (!nodeIsClassic && !nodeIsUnified) { // regular codepath (ie. not classic/unified)
1392 } else { // this is a classic run
1393 Warning ("Removing classic from '{0}' ... will be removed in the unified run if not present there.", signature);
1394 member.RemoveApiStyle (ApiStyle.Classic);
1395 if (classicAssemblyInfo != null) {
1396 member.RemoveChild (classicAssemblyInfo);
1402 class MemberComparer : XmlNodeComparer {
1403 public override int Compare (XmlNode x, XmlNode y)
1406 string xMemberName = x.Attributes ["MemberName"].Value;
1407 string yMemberName = y.Attributes ["MemberName"].Value;
1409 // generic methods *end* with '>'
1410 // it's possible for explicitly implemented generic interfaces to
1411 // contain <...> without being a generic method
1412 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1413 (r = xMemberName.CompareTo (yMemberName)) != 0)
1417 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1418 xMemberName = xMemberName.Substring (0, lt);
1419 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1420 yMemberName = yMemberName.Substring (0, lt);
1421 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1424 // if @MemberName matches, then it's either two different types of
1425 // members sharing the same name, e.g. field & property, or it's an
1426 // overloaded method.
1427 // for different type, sort based on MemberType value.
1428 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1429 y.SelectSingleNode ("MemberType").InnerText);
1433 // same type -- must be an overloaded method. Sort based on type
1434 // parameter count, then parameter count, then by the parameter
1436 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1437 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1438 if (xTypeParams.Count != yTypeParams.Count)
1439 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1440 for (int i = 0; i < xTypeParams.Count; ++i) {
1441 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1442 yTypeParams [i].Attributes ["Name"].Value);
1447 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1448 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1449 if (xParams.Count != yParams.Count)
1450 return xParams.Count <= yParams.Count ? -1 : 1;
1451 for (int i = 0; i < xParams.Count; ++i) {
1452 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1453 yParams [i].Attributes ["Type"].Value);
1457 // all parameters match, but return value might not match if it was
1458 // changed between one version and another.
1459 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1460 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1461 if (xReturn != null && yReturn != null) {
1462 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1471 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1473 private static void SortTypeMembers (XmlNode members)
1475 if (members == null)
1477 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1480 private static bool MemberDocsHaveUserContent (XmlNode e)
1482 e = (XmlElement)e.SelectSingleNode("Docs");
1483 if (e == null) return false;
1484 foreach (XmlElement d in e.SelectNodes("*"))
1485 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1490 // UPDATE HELPER FUNCTIONS
1492 // CREATE A STUB DOCUMENTATION FILE
1494 public XmlElement StubType (TypeDefinition type, string output)
1496 string typesig = typeFormatters [0].GetDeclaration (type);
1497 if (typesig == null) return null; // not publicly visible
1499 XmlDocument doc = new XmlDocument();
1500 XmlElement root = doc.CreateElement("Type");
1501 doc.AppendChild (root);
1503 DoUpdateType2 ("New Type", doc, type, output, true);
1508 private XmlElement CreateSinceNode (XmlDocument doc)
1510 XmlElement s = doc.CreateElement ("since");
1511 s.SetAttribute ("version", since);
1515 // STUBBING/UPDATING FUNCTIONS
1517 public void UpdateType (XmlElement root, TypeDefinition type)
1519 root.SetAttribute("Name", GetDocTypeName (type));
1520 root.SetAttribute("FullName", GetDocTypeFullName (type));
1522 foreach (MemberFormatter f in typeFormatters) {
1523 string element = "TypeSignature[@Language='" + f.Language + "']";
1524 string valueToUse = f.GetDeclaration (type);
1527 root.SelectNodes (element).Cast<XmlElement> ().ToArray (),
1528 x => x.GetAttribute ("Value") == valueToUse,
1529 x => x.SetAttribute ("Value", valueToUse),
1531 var node = WriteElementAttribute (root, element, "Language", f.Language, forceNewElement: true);
1532 var newnode = WriteElementAttribute (root, node, "Value", valueToUse);
1538 string assemblyInfoNodeFilter = MDocUpdater.HasDroppedNamespace (type) ? "[@apistyle='unified']" : "[not(@apistyle) or @apistyle='classic']";
1541 root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ().ToArray (),
1542 x => x.SelectSingleNode("AssemblyName").InnerText == type.Module.Assembly.Name.Name,
1543 x => WriteElementText(x, "AssemblyName", type.Module.Assembly.Name.Name),
1545 XmlElement ass = WriteElement(root, "AssemblyInfo", forceNewElement:true);
1547 if (MDocUpdater.HasDroppedNamespace (type)) ass.SetAttribute ("apistyle", "unified");
1555 foreach(var ass in root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ())
1557 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1558 if (!no_assembly_versions) {
1559 UpdateAssemblyVersions (root, type, true);
1562 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1563 foreach (var version in versions)
1564 ass.RemoveChild (version);
1566 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1567 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1569 ClearElement(ass, "AssemblyCulture");
1572 // Why-oh-why do we put assembly attributes in each type file?
1573 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1574 // since they're outdated in current docs, and a waste of space.
1575 //MakeAttributes(ass, type.Assembly, true);
1576 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1577 if (assattrs != null)
1578 ass.RemoveChild(assattrs);
1580 NormalizeWhitespace(ass);
1583 if (type.IsGenericType ()) {
1584 MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
1586 ClearElement(root, "TypeParameters");
1589 if (type.BaseType != null) {
1590 XmlElement basenode = WriteElement(root, "Base");
1592 string basetypename = GetDocTypeFullName (type.BaseType);
1593 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1594 WriteElementText(root, "Base/BaseTypeName", basetypename);
1596 // Document how this type instantiates the generic parameters of its base type
1597 TypeReference origBase = type.BaseType.GetElementType ();
1598 if (origBase.IsGenericType ()) {
1599 ClearElement(basenode, "BaseTypeArguments");
1600 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1601 IList<TypeReference> baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1602 IList<GenericParameter> baseGenParams = origBase.GenericParameters;
1603 if (baseGenArgs.Count != baseGenParams.Count)
1604 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1605 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1606 GenericParameter param = baseGenParams [i];
1607 TypeReference value = baseGenArgs [i];
1609 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1610 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1611 bta.AppendChild(arg);
1612 arg.SetAttribute ("TypeParamName", param.Name);
1613 arg.InnerText = GetDocTypeFullName (value);
1617 ClearElement(root, "Base");
1620 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1621 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1622 List<string> interface_names = userInterfaces
1623 .Select (iface => GetDocTypeFullName (iface))
1627 XmlElement interfaces = WriteElement(root, "Interfaces");
1628 interfaces.RemoveAll();
1629 foreach (string iname in interface_names) {
1630 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1631 interfaces.AppendChild(iface);
1632 WriteElementText(iface, "InterfaceName", iname);
1635 ClearElement(root, "Interfaces");
1638 MakeAttributes (root, GetCustomAttributes (type), type);
1640 if (DocUtils.IsDelegate (type)) {
1641 MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
1642 var member = type.GetMethod ("Invoke");
1643 MakeParameters(root, member, member.Parameters);
1644 MakeReturnValue(root, member);
1647 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1648 MakeDocNode (typeInfo);
1650 if (!DocUtils.IsDelegate (type))
1651 WriteElement (root, "Members");
1653 OrderTypeNodes (root, root.ChildNodes);
1654 NormalizeWhitespace(root);
1657 static readonly string[] TypeNodeOrder = {
1661 "ThreadingSafetyStatement",
1662 "ThreadSafetyStatement",
1674 static void OrderTypeNodes (XmlNode member, XmlNodeList children)
1676 ReorderNodes (member, children, TypeNodeOrder);
1679 internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1681 List<T> l = new List<T> (list);
1686 private void UpdateMember (DocsNodeInfo info)
1688 XmlElement me = (XmlElement) info.Node;
1689 MemberReference mi = info.Member;
1691 foreach (MemberFormatter f in memberFormatters) {
1692 string element = "MemberSignature[@Language='" + f.Language + "']";
1694 var valueToUse = f.GetDeclaration (mi);
1697 me.SelectNodes (element).Cast<XmlElement> ().ToArray(),
1698 x => x.GetAttribute("Value") == valueToUse,
1699 x => x.SetAttribute ("Value", valueToUse),
1701 var node = WriteElementAttribute (me, element, "Language", f.Language, forceNewElement:true);
1702 var newNode = WriteElementAttribute (me, node, "Value", valueToUse);
1709 WriteElementText(me, "MemberType", GetMemberType(mi));
1711 if (!no_assembly_versions) {
1712 UpdateAssemblyVersions (me, mi, true);
1715 ClearElement (me, "AssemblyInfo");
1718 MakeAttributes (me, GetCustomAttributes (mi), mi.DeclaringType);
1720 MakeReturnValue(me, mi, MDocUpdater.HasDroppedNamespace(mi));
1721 if (mi is MethodReference) {
1722 MethodReference mb = (MethodReference) mi;
1723 if (mb.IsGenericMethod ())
1724 MakeTypeParameters (me, mb.GenericParameters, mi, MDocUpdater.HasDroppedNamespace(mi));
1726 MakeParameters(me, mi, MDocUpdater.HasDroppedNamespace(mi));
1729 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1730 WriteElementText(me, "MemberValue", fieldValue);
1732 info.Node = WriteElement (me, "Docs");
1734 OrderMemberNodes (me, me.ChildNodes);
1735 UpdateExtensionMethods (me, info);
1738 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, MemberReference member) {
1739 AddXmlNode (relevant, valueMatches, setValue, makeNewNode, member.Module);
1742 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, TypeDefinition type) {
1743 AddXmlNode (relevant, valueMatches, setValue, makeNewNode, type.Module);
1746 /// <summary>Adds an xml node, reusing the node if it's available</summary>
1747 /// <param name="relevant">The existing set of nodes</param>
1748 /// <param name="valueMatches">Checks to see if the node's value matches what you're trying to write.</param>
1749 /// <param name="setValue">Sets the node's value</param>
1750 /// <param name="makeNewNode">Creates a new node, if valueMatches returns false.</param>
1751 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, ModuleDefinition module)
1753 bool shouldDuplicate = MDocUpdater.HasDroppedNamespace (module);
1754 var styleToUse = shouldDuplicate ? ApiStyle.Unified : ApiStyle.Classic;
1755 var existing = relevant;
1757 bool addedOldApiStyle = false;
1759 if (shouldDuplicate) {
1760 existing = existing.Where (n => n.HasApiStyle (styleToUse)).ToArray ();
1761 foreach (var n in relevant.Where (n => n.DoesNotHaveApiStyle (styleToUse))) {
1762 if (valueMatches (n)) {
1766 n.AddApiStyle (ApiStyle.Classic);
1767 addedOldApiStyle = true;
1772 if (!existing.Any ()) {
1773 var newNode = makeNewNode ();
1774 if (shouldDuplicate && addedOldApiStyle) {
1775 newNode.AddApiStyle (ApiStyle.Unified);
1779 var itemToReuse = existing.First ();
1780 setValue (itemToReuse);
1782 if (shouldDuplicate && addedOldApiStyle) {
1783 itemToReuse.AddApiStyle (styleToUse);
1789 static readonly string[] MemberNodeOrder = {
1804 static void OrderMemberNodes (XmlNode member, XmlNodeList children)
1806 ReorderNodes (member, children, MemberNodeOrder);
1809 static void ReorderNodes (XmlNode node, XmlNodeList children, string[] ordering)
1811 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1812 for (int i = 0; i < ordering.Length; ++i) {
1813 for (int j = 0; j < children.Count; ++j) {
1814 XmlNode c = children [j];
1815 if (c.Name == ordering [i]) {
1816 newChildren.Add (c);
1820 if (newChildren.Count >= 0)
1821 node.PrependChild ((XmlNode) newChildren [0]);
1822 for (int i = 1; i < newChildren.Count; ++i) {
1823 XmlNode prev = (XmlNode) newChildren [i-1];
1824 XmlNode cur = (XmlNode) newChildren [i];
1825 node.RemoveChild (cur);
1826 node.InsertAfter (cur, prev);
1830 IEnumerable<string> GetCustomAttributes (MemberReference mi)
1832 IEnumerable<string> attrs = Enumerable.Empty<string>();
1834 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1836 attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1838 PropertyDefinition pd = mi as PropertyDefinition;
1840 if (pd.GetMethod != null)
1841 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1842 if (pd.SetMethod != null)
1843 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1846 EventDefinition ed = mi as EventDefinition;
1848 if (ed.AddMethod != null)
1849 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1850 if (ed.RemoveMethod != null)
1851 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1857 IEnumerable<string> GetCustomAttributes (IList<CustomAttribute> attributes, string prefix)
1859 foreach (CustomAttribute attribute in attributes.OrderBy (ca => ca.AttributeType.FullName)) {
1861 TypeDefinition attrType = attribute.AttributeType as TypeDefinition;
1862 if (attrType != null && !IsPublic (attrType))
1864 if (slashdocFormatter.GetName (attribute.AttributeType) == null)
1867 if (Array.IndexOf (IgnorableAttributes, attribute.AttributeType.FullName) >= 0)
1870 StringList fields = new StringList ();
1872 for (int i = 0; i < attribute.ConstructorArguments.Count; ++i) {
1873 CustomAttributeArgument argument = attribute.ConstructorArguments [i];
1874 fields.Add (MakeAttributesValueString (
1879 (from namedArg in attribute.Fields
1880 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value })
1882 (from namedArg in attribute.Properties
1883 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value }))
1884 .OrderBy (v => v.Name);
1885 foreach (var d in namedArgs)
1886 fields.Add (string.Format ("{0}={1}", d.Name,
1887 MakeAttributesValueString (d.Value, d.Type)));
1889 string a2 = String.Join(", ", fields.ToArray ());
1890 if (a2 != "") a2 = "(" + a2 + ")";
1892 string name = attribute.GetDeclaringType();
1893 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1894 yield return prefix + name + a2;
1898 static readonly string[] ValidExtensionMembers = {
1907 static readonly string[] ValidExtensionDocMembers = {
1913 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1915 MethodDefinition me = info.Member as MethodDefinition;
1918 if (info.Parameters.Count < 1)
1920 if (!DocUtils.IsExtensionMethod (me))
1923 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1924 XmlNode member = e.CloneNode (true);
1925 em.AppendChild (member);
1926 RemoveExcept (member, ValidExtensionMembers);
1927 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1928 WriteElementText (member, "MemberType", "ExtensionMethod");
1929 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1930 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1931 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1932 member.AppendChild (link);
1933 AddTargets (em, info);
1935 extensionMethods.Add (em);
1938 private static void RemoveExcept (XmlNode node, string[] except)
1942 MyXmlNodeList remove = null;
1943 foreach (XmlNode n in node.ChildNodes) {
1944 if (Array.BinarySearch (except, n.Name) < 0) {
1946 remove = new MyXmlNodeList ();
1951 foreach (XmlNode n in remove)
1952 node.RemoveChild (n);
1955 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1957 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1958 member.PrependChild (targets);
1959 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1960 AppendElementAttributeText (targets, "Target", "Type",
1961 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1964 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1965 IList<TypeReference> constraints = gp.Constraints;
1966 if (constraints.Count == 0)
1967 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1969 foreach (TypeReference c in constraints)
1970 AppendElementAttributeText(targets, "Target", "Type",
1971 slashdocFormatter.GetDeclaration (c));
1975 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1978 TypeDefinition type = field.DeclaringType.Resolve ();
1979 if (type != null && type.IsEnum) return false;
1981 if (type != null && type.IsGenericType ()) return false;
1982 if (!field.HasConstant)
1984 if (field.IsLiteral) {
1985 object val = field.Constant;
1986 if (val == null) value = "null";
1987 else if (val is Enum) value = val.ToString();
1988 else if (val is IFormattable) {
1989 value = ((IFormattable)val).ToString();
1991 value = "\"" + value + "\"";
1993 if (value != null && value != "")
1999 // XML HELPER FUNCTIONS
2001 internal static XmlElement WriteElement(XmlNode parent, string element, bool forceNewElement = false) {
2002 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
2003 if (ret == null || forceNewElement) {
2004 string[] path = element.Split('/');
2005 foreach (string p in path) {
2006 ret = (XmlElement)parent.SelectSingleNode(p);
2007 if (ret == null || forceNewElement) {
2009 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
2010 ename = ename.Substring(0, ename.IndexOf('['));
2011 ret = parent.OwnerDocument.CreateElement(ename);
2012 parent.AppendChild(ret);
2021 private static XmlElement WriteElementText(XmlNode parent, string element, string value, bool forceNewElement = false) {
2022 XmlElement node = WriteElement(parent, element, forceNewElement: forceNewElement);
2023 node.InnerText = value;
2027 static XmlElement AppendElementText (XmlNode parent, string element, string value)
2029 XmlElement n = parent.OwnerDocument.CreateElement (element);
2030 parent.AppendChild (n);
2031 n.InnerText = value;
2035 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
2037 XmlElement n = parent.OwnerDocument.CreateElement (element);
2038 parent.AppendChild (n);
2039 n.SetAttribute (attribute, value);
2043 internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
2045 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
2046 dest.AppendChild (copy);
2050 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
2051 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
2054 node = WriteElement(parent, element);
2055 node.InnerText = value;
2057 private static XmlElement WriteElementAttribute(XmlElement parent, string element, string attribute, string value, bool forceNewElement = false) {
2058 XmlElement node = WriteElement(parent, element, forceNewElement:forceNewElement);
2059 return WriteElementAttribute (parent, node, attribute, value);
2061 private static XmlElement WriteElementAttribute(XmlElement parent, XmlElement node, string attribute, string value) {
2062 if (node.GetAttribute (attribute) != value) {
2063 node.SetAttribute (attribute, value);
2067 internal static void ClearElement(XmlElement parent, string name) {
2068 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
2070 parent.RemoveChild(node);
2073 // DOCUMENTATION HELPER FUNCTIONS
2075 private void MakeDocNode (DocsNodeInfo info)
2077 List<GenericParameter> genericParams = info.GenericParameters;
2078 IList<ParameterDefinition> parameters = info.Parameters;
2079 TypeReference returntype = info.ReturnType;
2080 bool returnisreturn = info.ReturnIsReturn;
2081 XmlElement e = info.Node;
2082 bool addremarks = info.AddRemarks;
2084 WriteElementInitialText(e, "summary", "To be added.");
2086 if (parameters != null) {
2087 string[] values = new string [parameters.Count];
2088 for (int i = 0; i < values.Length; ++i)
2089 values [i] = parameters [i].Name;
2090 UpdateParameters (e, "param", values);
2093 if (genericParams != null) {
2094 string[] values = new string [genericParams.Count];
2095 for (int i = 0; i < values.Length; ++i)
2096 values [i] = genericParams [i].Name;
2097 UpdateParameters (e, "typeparam", values);
2100 string retnodename = null;
2101 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
2102 retnodename = returnisreturn ? "returns" : "value";
2103 string retnodename_other = !returnisreturn ? "returns" : "value";
2105 // If it has a returns node instead of a value node, change its name.
2106 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
2107 if (retother != null) {
2108 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
2109 foreach (XmlNode node in retother)
2110 retnode.AppendChild(node.CloneNode(true));
2111 e.ReplaceChild(retnode, retother);
2113 WriteElementInitialText(e, retnodename, "To be added.");
2116 ClearElement(e, "returns");
2117 ClearElement(e, "value");
2121 WriteElementInitialText(e, "remarks", "To be added.");
2123 if (exceptions.HasValue && info.Member != null &&
2124 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
2125 UpdateExceptions (e, info.Member);
2128 foreach (DocumentationImporter importer in importers)
2129 importer.ImportDocumentation (info);
2131 OrderDocsNodes (e, e.ChildNodes);
2132 NormalizeWhitespace(e);
2135 static readonly string[] DocsNodeOrder = {
2136 "typeparam", "param", "summary", "returns", "value", "remarks",
2139 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
2141 ReorderNodes (docs, children, DocsNodeOrder);
2145 private void UpdateParameters (XmlElement e, string element, string[] values)
2147 if (values != null) {
2148 XmlNode[] paramnodes = new XmlNode[values.Length];
2150 // Some documentation had param nodes with leading spaces.
2151 foreach (XmlElement paramnode in e.SelectNodes(element)){
2152 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
2155 // If a member has only one parameter, we can track changes to
2156 // the name of the parameter easily.
2157 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
2158 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
2161 bool reinsert = false;
2163 // Pick out existing and still-valid param nodes, and
2164 // create nodes for parameters not in the file.
2165 Hashtable seenParams = new Hashtable();
2166 for (int pi = 0; pi < values.Length; pi++) {
2167 string p = values [pi];
2170 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
2171 if (paramnodes[pi] != null) continue;
2173 XmlElement pe = e.OwnerDocument.CreateElement(element);
2174 pe.SetAttribute("name", p);
2175 pe.InnerText = "To be added.";
2176 paramnodes[pi] = pe;
2180 // Remove parameters that no longer exist and check all params are in the right order.
2182 MyXmlNodeList todelete = new MyXmlNodeList ();
2183 foreach (XmlElement paramnode in e.SelectNodes(element)) {
2184 string name = paramnode.GetAttribute("name");
2185 if (!seenParams.ContainsKey(name)) {
2186 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2187 Warning ("The following param node can only be deleted if the --delete option is given: ");
2188 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
2190 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
2191 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2195 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
2196 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2197 e.ParentNode.Attributes ["MemberName"].Value,
2200 Warning ("\tValue={0}", paramnode.OuterXml);
2202 todelete.Add (paramnode);
2207 if ((int)seenParams[name] != idx)
2213 foreach (XmlNode n in todelete) {
2214 n.ParentNode.RemoveChild (n);
2217 // Re-insert the parameter nodes at the top of the doc section.
2219 for (int pi = values.Length-1; pi >= 0; pi--)
2220 e.PrependChild(paramnodes[pi]);
2222 // Clear all existing param nodes
2223 foreach (XmlNode paramnode in e.SelectNodes(element)) {
2224 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2225 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
2226 Console.WriteLine(paramnode.OuterXml);
2228 paramnode.ParentNode.RemoveChild(paramnode);
2234 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
2236 string existingName = pe.GetAttribute ("name");
2237 pe.SetAttribute ("name", newName);
2238 if (existingName == newName)
2240 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
2241 if (paramref.GetAttribute ("name").Trim () == existingName)
2242 paramref.SetAttribute ("name", newName);
2245 class CrefComparer : XmlNodeComparer {
2247 public CrefComparer ()
2251 public override int Compare (XmlNode x, XmlNode y)
2253 string xType = x.Attributes ["cref"].Value;
2254 string yType = y.Attributes ["cref"].Value;
2255 string xNamespace = GetNamespace (xType);
2256 string yNamespace = GetNamespace (yType);
2258 int c = xNamespace.CompareTo (yNamespace);
2261 return xType.CompareTo (yType);
2264 static string GetNamespace (string type)
2266 int n = type.LastIndexOf ('.');
2268 return type.Substring (0, n);
2269 return string.Empty;
2273 private void UpdateExceptions (XmlNode docs, MemberReference member)
2275 string indent = new string (' ', 10);
2276 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
2277 string cref = slashdocFormatter.GetDeclaration (source.Exception);
2278 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
2281 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
2282 e.SetAttribute ("cref", cref);
2283 e.InnerXml = "To be added; from:\n" + indent + "<see cref=\"" +
2284 string.Join ("\" />,\n" + indent + "<see cref=\"",
2285 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
2286 .OrderBy (s => s)) +
2288 docs.AppendChild (e);
2290 SortXmlNodes (docs, docs.SelectNodes ("exception"),
2291 new CrefComparer ());
2294 private static void NormalizeWhitespace(XmlElement e) {
2295 // Remove all text and whitespace nodes from the element so it
2296 // is outputted with nice indentation and no blank lines.
2297 ArrayList deleteNodes = new ArrayList();
2298 foreach (XmlNode n in e)
2299 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2301 foreach (XmlNode n in deleteNodes)
2302 n.ParentNode.RemoveChild(n);
2305 private static bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
2307 TypeDefinition type = member as TypeDefinition;
2309 type = member.DeclaringType as TypeDefinition;
2310 return UpdateAssemblyVersions(root, type.Module.Assembly, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
2313 private static string GetAssemblyVersion (AssemblyDefinition assembly)
2315 return assembly.Name.Version.ToString();
2318 private static bool UpdateAssemblyVersions(XmlElement root, AssemblyDefinition assembly, string[] assemblyVersions, bool add)
2320 XmlElement av = (XmlElement) root.SelectSingleNode ("AssemblyVersions");
2322 // AssemblyVersions is not part of the spec
2323 root.RemoveChild (av);
2326 string oldNodeFilter = "AssemblyInfo[not(@apistyle) or @apistyle='classic']";
2327 string newNodeFilter = "AssemblyInfo[@apistyle='unified']";
2328 string thisNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? newNodeFilter : oldNodeFilter;
2329 string thatNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? oldNodeFilter : newNodeFilter;
2331 XmlElement e = (XmlElement) root.SelectSingleNode (thisNodeFilter);
2333 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2335 if (MDocUpdater.HasDroppedNamespace (assembly)) {
2336 e.SetAttribute ("apistyle", "unified");
2339 root.AppendChild(e);
2342 var thatNode = (XmlElement) root.SelectSingleNode (thatNodeFilter);
2343 if (MDocUpdater.HasDroppedNamespace (assembly) && thatNode != null) {
2344 // there's a classic node, we should add apistyles
2345 e.SetAttribute ("apistyle", "unified");
2346 thatNode.SetAttribute ("apistyle", "classic");
2349 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
2350 .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
2352 // matches.Count > 0 && add: ignore -- already present
2353 if (matches.Count > 0 && !add) {
2354 foreach (XmlNode c in matches)
2357 else if (matches.Count == 0 && add) {
2358 foreach (string sv in assemblyVersions) {
2359 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2365 // matches.Count == 0 && !add: ignore -- already not present
2366 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2367 SortXmlNodes (e, avs, new VersionComparer ());
2369 bool anyNodesLeft = avs.Count != 0;
2370 if (!anyNodesLeft) {
2371 e.ParentNode.RemoveChild (e);
2373 return anyNodesLeft;
2376 // FIXME: get TypeReferences instead of string comparison?
2377 private static string[] IgnorableAttributes = {
2378 // Security related attributes
2379 "System.Reflection.AssemblyKeyFileAttribute",
2380 "System.Reflection.AssemblyDelaySignAttribute",
2381 // Present in @RefType
2382 "System.Runtime.InteropServices.OutAttribute",
2383 // For naming the indexer to use when not using indexers
2384 "System.Reflection.DefaultMemberAttribute",
2385 // for decimal constants
2386 "System.Runtime.CompilerServices.DecimalConstantAttribute",
2387 // compiler generated code
2388 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
2389 // more compiler generated code, e.g. iterator methods
2390 "System.Diagnostics.DebuggerHiddenAttribute",
2391 "System.Runtime.CompilerServices.FixedBufferAttribute",
2392 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
2393 // extension methods
2394 "System.Runtime.CompilerServices.ExtensionAttribute",
2395 // Used to differentiate 'object' from C#4 'dynamic'
2396 "System.Runtime.CompilerServices.DynamicAttribute",
2399 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes, TypeReference t=null)
2401 if (!attributes.Any ()) {
2402 ClearElement (root, "Attributes");
2406 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2410 e = root.OwnerDocument.CreateElement("Attributes");
2412 foreach (string attribute in attributes) {
2413 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2416 WriteElementText(ae, "AttributeName", attribute);
2419 if (e.ParentNode == null)
2420 root.AppendChild(e);
2422 NormalizeWhitespace(e);
2425 public static string MakeAttributesValueString (object v, TypeReference valueType)
2427 var formatters = new [] {
2428 new AttributeValueFormatter (),
2429 new ApplePlatformEnumFormatter (),
2430 new StandardFlagsEnumFormatter (),
2431 new DefaultAttributeValueFormatter (),
2434 ResolvedTypeInfo type = new ResolvedTypeInfo (valueType);
2435 foreach (var formatter in formatters) {
2436 string formattedValue;
2437 if (formatter.TryFormatValue (v, type, out formattedValue)) {
2438 return formattedValue;
2442 // this should never occur because the DefaultAttributeValueFormatter will always
2443 // successfully format the value ... but this is needed to satisfy the compiler :)
2444 throw new InvalidDataException (string.Format ("Unable to format attribute value ({0})", v.ToString ()));
2447 internal static IDictionary<long, string> GetEnumerationValues (TypeDefinition type)
2449 var values = new Dictionary<long, string> ();
2451 (from f in type.Fields
2452 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
2454 values [ToInt64 (f.Constant)] = f.Name;
2459 internal static long ToInt64 (object value)
2462 return (long) (ulong) value;
2463 return Convert.ToInt64 (value);
2466 private void MakeParameters (XmlElement root, MemberReference member, IList<ParameterDefinition> parameters, bool shouldDuplicateWithNew=false)
2468 XmlElement e = WriteElement(root, "Parameters");
2471 foreach (ParameterDefinition p in parameters) {
2475 var ptype = GetDocParameterType (p.ParameterType);
2476 var newPType = ptype;
2478 if (MDocUpdater.SwitchingToMagicTypes) {
2479 newPType = NativeTypeManager.ConvertFromNativeType (ptype);
2482 // now find the existing node, if it's there so we can reuse it.
2483 var nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2484 .Cast<XmlElement> ().Where (x => x.GetAttribute ("Name") == p.Name)
2487 if (nodes.Count () == 0) {
2488 // wasn't found, let's make sure it wasn't just cause the param name was changed
2489 nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2490 .Cast<XmlElement> ()
2491 .Skip (i) // this makes sure we don't inadvertently "reuse" nodes when adding new ones
2492 .Where (x => x.GetAttribute ("Name") != p.Name && (x.GetAttribute ("Type") == ptype || x.GetAttribute ("Type") == newPType))
2493 .Take(1) // there might be more than one that meets this parameter ... only take the first.
2498 x => x.GetAttribute ("Type") == ptype,
2499 x => x.SetAttribute ("Type", ptype),
2501 pe = root.OwnerDocument.CreateElement ("Parameter");
2504 pe.SetAttribute ("Name", p.Name);
2505 pe.SetAttribute ("Type", ptype);
2506 if (p.ParameterType is ByReferenceType) {
2508 pe.SetAttribute ("RefType", "out");
2510 pe.SetAttribute ("RefType", "ref");
2513 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
2522 private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams, MemberReference member, bool shouldDuplicateWithNew)
2524 if (typeParams == null || typeParams.Count == 0) {
2525 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2527 root.RemoveChild (f);
2530 XmlElement e = WriteElement(root, "TypeParameters");
2532 var nodes = e.SelectNodes ("TypeParameter").Cast<XmlElement> ().ToArray ();
2534 foreach (GenericParameter t in typeParams) {
2536 IList<TypeReference> constraints = t.Constraints;
2537 GenericParameterAttributes attrs = t.Attributes;
2543 var baseType = e.SelectSingleNode("BaseTypeName");
2544 // TODO: should this comparison take into account BaseTypeName?
2545 return x.GetAttribute("Name") == t.Name;
2547 x => {}, // no additional action required
2550 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2552 pe.SetAttribute("Name", t.Name);
2553 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""), t.DeclaringType);
2554 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2555 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2563 ce = root.OwnerDocument.CreateElement ("Constraints");
2565 pe.AppendChild (ce);
2566 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2567 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2568 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2569 AppendElementText (ce, "ParameterAttribute", "Covariant");
2570 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2571 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2572 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2573 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2574 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2575 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2576 foreach (TypeReference c in constraints) {
2577 TypeDefinition cd = c.Resolve ();
2578 AppendElementText (ce,
2579 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2580 GetDocTypeFullName (c));
2589 private void MakeParameters (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew)
2591 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2592 MakeParameters (root, mi, ((MethodDefinition)mi).Parameters, shouldDuplicateWithNew);
2593 else if (mi is MethodDefinition) {
2594 MethodDefinition mb = (MethodDefinition) mi;
2595 IList<ParameterDefinition> parameters = mb.Parameters;
2596 MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
2597 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2598 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2599 p.SetAttribute ("RefType", "this");
2602 else if (mi is PropertyDefinition) {
2603 IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
2604 if (parameters.Count > 0)
2605 MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
2609 else if (mi is FieldDefinition) return;
2610 else if (mi is EventDefinition) return;
2611 else throw new ArgumentException();
2614 internal static string GetDocParameterType (TypeReference type)
2616 return GetDocTypeFullName (type).Replace ("@", "&");
2619 private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes, bool shouldDuplicateWithNew=false)
2621 XmlElement e = WriteElement(root, "ReturnValue");
2622 var valueToUse = GetDocTypeFullName (type);
2624 AddXmlNode (e.SelectNodes("ReturnType").Cast<XmlElement> ().ToArray (),
2625 x => x.InnerText == valueToUse,
2626 x => x.InnerText = valueToUse,
2628 var newNode = WriteElementText(e, "ReturnType", valueToUse, forceNewElement: true);
2629 if (attributes != null)
2630 MakeAttributes(e, GetCustomAttributes (attributes, ""), type);
2637 private void MakeReturnValue (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew=false)
2639 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2641 else if (mi is MethodDefinition)
2642 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes, shouldDuplicateWithNew);
2643 else if (mi is PropertyDefinition)
2644 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null, shouldDuplicateWithNew);
2645 else if (mi is FieldDefinition)
2646 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null, shouldDuplicateWithNew);
2647 else if (mi is EventDefinition)
2648 MakeReturnValue (root, ((EventDefinition)mi).EventType, null, shouldDuplicateWithNew);
2650 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2653 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2655 MemberReference mi = info.Member;
2656 if (mi is TypeDefinition) return null;
2658 string sigs = memberFormatters [0].GetDeclaration (mi);
2659 if (sigs == null) return null; // not publicly visible
2661 // no documentation for property/event accessors. Is there a better way of doing this?
2662 if (mi.Name.StartsWith("get_")) return null;
2663 if (mi.Name.StartsWith("set_")) return null;
2664 if (mi.Name.StartsWith("add_")) return null;
2665 if (mi.Name.StartsWith("remove_")) return null;
2666 if (mi.Name.StartsWith("raise_")) return null;
2668 XmlElement me = doc.CreateElement("Member");
2669 me.SetAttribute("MemberName", GetMemberName (mi));
2673 if (exceptions.HasValue &&
2674 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2675 UpdateExceptions (info.Node, info.Member);
2677 if (since != null) {
2678 XmlNode docs = me.SelectSingleNode("Docs");
2679 docs.AppendChild (CreateSinceNode (doc));
2685 internal static string GetMemberName (MemberReference mi)
2687 MethodDefinition mb = mi as MethodDefinition;
2689 PropertyDefinition pi = mi as PropertyDefinition;
2692 return DocUtils.GetPropertyName (pi);
2694 StringBuilder sb = new StringBuilder (mi.Name.Length);
2695 if (!DocUtils.IsExplicitlyImplemented (mb))
2696 sb.Append (mi.Name);
2698 TypeReference iface;
2699 MethodReference ifaceMethod;
2700 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2701 sb.Append (GetDocTypeFullName (iface));
2703 sb.Append (ifaceMethod.Name);
2705 if (mb.IsGenericMethod ()) {
2706 IList<GenericParameter> typeParams = mb.GenericParameters;
2707 if (typeParams.Count > 0) {
2709 sb.Append (typeParams [0].Name);
2710 for (int i = 1; i < typeParams.Count; ++i)
2711 sb.Append (",").Append (typeParams [i].Name);
2715 return sb.ToString ();
2718 /// SIGNATURE GENERATION FUNCTIONS
2719 internal static bool IsPrivate (MemberReference mi)
2721 return memberFormatters [0].GetDeclaration (mi) == null;
2724 internal static string GetMemberType (MemberReference mi)
2726 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2727 return "Constructor";
2728 if (mi is MethodDefinition)
2730 if (mi is PropertyDefinition)
2732 if (mi is FieldDefinition)
2734 if (mi is EventDefinition)
2736 throw new ArgumentException();
2739 private static string GetDocTypeName (TypeReference type)
2741 return docTypeFormatter.GetName (type);
2744 internal static string GetDocTypeFullName (TypeReference type)
2746 return DocTypeFullMemberFormatter.Default.GetName (type);
2749 internal static string GetXPathForMember (DocumentationMember member)
2751 StringBuilder xpath = new StringBuilder ();
2752 xpath.Append ("//Members/Member[@MemberName=\"")
2753 .Append (member.MemberName)
2755 if (member.Parameters != null && member.Parameters.Count > 0) {
2756 xpath.Append ("/Parameters[count(Parameter) = ")
2757 .Append (member.Parameters.Count);
2758 for (int i = 0; i < member.Parameters.Count; ++i) {
2759 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2760 xpath.Append (member.Parameters [i]);
2761 xpath.Append ("\"");
2763 xpath.Append ("]/..");
2765 return xpath.ToString ();
2768 public static string GetXPathForMember (XPathNavigator member)
2770 StringBuilder xpath = new StringBuilder ();
2771 xpath.Append ("//Type[@FullName=\"")
2772 .Append (member.SelectSingleNode ("../../@FullName").Value)
2774 xpath.Append ("Members/Member[@MemberName=\"")
2775 .Append (member.SelectSingleNode ("@MemberName").Value)
2777 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2778 if (parameters.Count > 0) {
2779 xpath.Append ("/Parameters[count(Parameter) = ")
2780 .Append (parameters.Count);
2782 while (parameters.MoveNext ()) {
2784 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2785 xpath.Append (parameters.Current.Value);
2786 xpath.Append ("\"");
2788 xpath.Append ("]/..");
2790 return xpath.ToString ();
2793 public static string GetXPathForMember (MemberReference member)
2795 StringBuilder xpath = new StringBuilder ();
2796 xpath.Append ("//Type[@FullName=\"")
2797 .Append (member.DeclaringType.FullName)
2799 xpath.Append ("Members/Member[@MemberName=\"")
2800 .Append (GetMemberName (member))
2803 IList<ParameterDefinition> parameters = null;
2804 if (member is MethodDefinition)
2805 parameters = ((MethodDefinition) member).Parameters;
2806 else if (member is PropertyDefinition) {
2807 parameters = ((PropertyDefinition) member).Parameters;
2809 if (parameters != null && parameters.Count > 0) {
2810 xpath.Append ("/Parameters[count(Parameter) = ")
2811 .Append (parameters.Count);
2812 for (int i = 0; i < parameters.Count; ++i) {
2813 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2814 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2815 xpath.Append ("\"");
2817 xpath.Append ("]/..");
2819 return xpath.ToString ();
2823 static class CecilExtensions {
2824 public static string GetDeclaringType(this CustomAttribute attribute)
2826 var type = attribute.Constructor.DeclaringType;
2827 var typeName = type.FullName;
2829 string translatedType = NativeTypeManager.GetTranslatedName (type);
2830 return translatedType;
2833 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
2835 foreach (var c in type.Methods.Where (m => m.IsConstructor))
2836 yield return (MemberReference) c;
2837 foreach (var e in type.Events)
2838 yield return (MemberReference) e;
2839 foreach (var f in type.Fields)
2840 yield return (MemberReference) f;
2841 foreach (var m in type.Methods.Where (m => !m.IsConstructor))
2842 yield return (MemberReference) m;
2843 foreach (var t in type.NestedTypes)
2844 yield return (MemberReference) t;
2845 foreach (var p in type.Properties)
2846 yield return (MemberReference) p;
2849 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
2851 return GetMembers (type).Where (m => m.Name == member);
2854 public static MemberReference GetMember (this TypeDefinition type, string member)
2856 return GetMembers (type, member).EnsureZeroOrOne ();
2859 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2861 if (source.Count () > 1)
2862 throw new InvalidOperationException ("too many matches");
2863 return source.FirstOrDefault ();
2866 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2869 .Where (m => m.Name == method)
2870 .EnsureZeroOrOne ();
2873 public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
2875 TypeDefinition def = type as TypeDefinition;
2877 return new MemberReference [0];
2878 CustomAttribute defMemberAttr = def.CustomAttributes
2879 .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
2880 if (defMemberAttr == null)
2881 return new MemberReference [0];
2882 string name = (string) defMemberAttr.ConstructorArguments [0].Value;
2883 return def.Properties
2884 .Where (p => p.Name == name)
2885 .Select (p => (MemberReference) p);
2888 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2890 return assembly.Modules.SelectMany (md => md.GetAllTypes ());
2893 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2895 return GetTypes (assembly)
2896 .Where (td => td.FullName == type)
2897 .EnsureZeroOrOne ();
2900 public static bool IsGenericType (this TypeReference type)
2902 return type.GenericParameters.Count > 0;
2905 public static bool IsGenericMethod (this MethodReference method)
2907 return method.GenericParameters.Count > 0;
2910 public static MemberReference Resolve (this MemberReference member)
2912 FieldReference fr = member as FieldReference;
2914 return fr.Resolve ();
2915 MethodReference mr = member as MethodReference;
2917 return mr.Resolve ();
2918 TypeReference tr = member as TypeReference;
2920 return tr.Resolve ();
2921 PropertyReference pr = member as PropertyReference;
2924 EventReference er = member as EventReference;
2927 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2930 public static TypeReference GetUnderlyingType (this TypeDefinition type)
2934 return type.Fields.First (f => f.Name == "value__").FieldType;
2937 public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
2939 return self.Types.SelectMany (t => t.GetAllTypes ());
2942 static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
2946 if (!self.HasNestedTypes)
2949 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
2959 static class DocUtils {
2961 public static bool DoesNotHaveApiStyle(this XmlElement element, ApiStyle style) {
2962 string styleString = style.ToString ().ToLowerInvariant ();
2963 string apistylevalue = element.GetAttribute ("apistyle");
2964 return apistylevalue != styleString || string.IsNullOrWhiteSpace(apistylevalue);
2966 public static bool HasApiStyle(this XmlElement element, ApiStyle style) {
2967 string styleString = style.ToString ().ToLowerInvariant ();
2968 return element.GetAttribute ("apistyle") == styleString;
2970 public static bool HasApiStyle(this XmlNode node, ApiStyle style)
2972 var attribute = node.Attributes ["apistyle"];
2973 return attribute != null && attribute.Value == style.ToString ().ToLowerInvariant ();
2975 public static void AddApiStyle(this XmlElement element, ApiStyle style) {
2976 string styleString = style.ToString ().ToLowerInvariant ();
2977 var existingValue = element.GetAttribute ("apistyle");
2978 if (string.IsNullOrWhiteSpace (existingValue) || existingValue != styleString) {
2979 element.SetAttribute ("apistyle", styleString);
2982 public static void RemoveApiStyle (this XmlElement element, ApiStyle style)
2984 string styleString = style.ToString ().ToLowerInvariant ();
2985 string existingValue = element.GetAttribute ("apistyle");
2986 if (string.IsNullOrWhiteSpace (existingValue) || existingValue == styleString) {
2987 element.RemoveAttribute ("apistyle");
2990 public static void RemoveApiStyle (this XmlNode node, ApiStyle style)
2992 var styleAttribute = node.Attributes ["apistyle"];
2993 if (styleAttribute != null && styleAttribute.Value == style.ToString ().ToLowerInvariant ()) {
2994 node.Attributes.Remove (styleAttribute);
2998 public static bool IsExplicitlyImplemented (MethodDefinition method)
3000 return method.IsPrivate && method.IsFinal && method.IsVirtual;
3003 public static string GetTypeDotMember (string name)
3005 int startType, startMethod;
3006 startType = startMethod = -1;
3007 for (int i = 0; i < name.Length; ++i) {
3008 if (name [i] == '.') {
3009 startType = startMethod;
3013 return name.Substring (startType+1);
3016 public static string GetMember (string name)
3018 int i = name.LastIndexOf ('.');
3021 return name.Substring (i+1);
3024 public static void GetInfoForExplicitlyImplementedMethod (
3025 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
3029 if (method.Overrides.Count != 1)
3030 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
3031 iface = method.Overrides [0].DeclaringType;
3032 ifaceMethod = method.Overrides [0];
3035 public static string GetPropertyName (PropertyDefinition pi)
3037 // Issue: (g)mcs-generated assemblies that explicitly implement
3038 // properties don't specify the full namespace, just the
3039 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
3040 MethodDefinition method = pi.GetMethod;
3042 method = pi.SetMethod;
3043 if (!IsExplicitlyImplemented (method))
3046 // Need to determine appropriate namespace for this member.
3047 TypeReference iface;
3048 MethodReference ifaceMethod;
3049 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3050 return string.Join (".", new string[]{
3051 DocTypeFullMemberFormatter.Default.GetName (iface),
3052 GetMember (pi.Name)});
3055 public static string GetNamespace (TypeReference type)
3057 if (type.GetElementType ().IsNested)
3058 type = type.GetElementType ();
3059 while (type != null && type.IsNested)
3060 type = type.DeclaringType;
3062 return string.Empty;
3064 string typeNS = type.Namespace;
3066 // first, make sure this isn't a type reference to another assembly/module
3068 bool isInAssembly = MDocUpdater.IsInAssemblies(type.Module.Name);
3069 if (isInAssembly && !typeNS.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (type)) {
3070 typeNS = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typeNS);
3075 public static string PathCombine (string dir, string path)
3081 return Path.Combine (dir, path);
3084 public static bool IsExtensionMethod (MethodDefinition method)
3087 method.CustomAttributes
3088 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
3089 && method.DeclaringType.CustomAttributes
3090 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
3093 public static bool IsDelegate (TypeDefinition type)
3095 TypeReference baseRef = type.BaseType;
3096 if (baseRef == null)
3098 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
3099 baseRef.FullName == "System.MulticastDelegate";
3102 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
3104 List<TypeReference> decls = new List<TypeReference> ();
3106 while (type.DeclaringType != null) {
3107 decls.Add (type.DeclaringType);
3108 type = type.DeclaringType;
3114 public static int GetGenericArgumentCount (TypeReference type)
3116 GenericInstanceType inst = type as GenericInstanceType;
3118 ? inst.GenericArguments.Count
3119 : type.GenericParameters.Count;
3122 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
3124 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
3125 List<TypeReference> userInterfaces = new List<TypeReference> ();
3126 foreach (TypeReference iface in type.Interfaces) {
3127 TypeReference lookup = iface.Resolve () ?? iface;
3128 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
3129 userInterfaces.Add (iface);
3131 return userInterfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()));
3134 private static string GetQualifiedTypeName (TypeReference type)
3136 return "[" + type.Scope.Name + "]" + type.FullName;
3139 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
3141 HashSet<string> inheritedInterfaces = new HashSet<string> ();
3142 Action<TypeDefinition> a = null;
3144 if (t == null) return;
3145 foreach (TypeReference r in t.Interfaces) {
3146 inheritedInterfaces.Add (GetQualifiedTypeName (r));
3150 TypeReference baseRef = type.BaseType;
3151 while (baseRef != null) {
3152 TypeDefinition baseDef = baseRef.Resolve ();
3153 if (baseDef != null) {
3155 baseRef = baseDef.BaseType;
3160 foreach (TypeReference r in type.Interfaces)
3162 return inheritedInterfaces;
3166 class DocsNodeInfo {
3167 public DocsNodeInfo (XmlElement node)
3172 public DocsNodeInfo (XmlElement node, TypeDefinition type)
3178 public DocsNodeInfo (XmlElement node, MemberReference member)
3181 SetMemberInfo (member);
3184 void SetType (TypeDefinition type)
3187 throw new ArgumentNullException ("type");
3189 GenericParameters = new List<GenericParameter> (type.GenericParameters);
3190 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
3191 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
3192 for (int i = 0; i < declTypes.Count - 1; ++i) {
3193 int remove = System.Math.Min (maxGenArgs,
3194 DocUtils.GetGenericArgumentCount (declTypes [i]));
3195 maxGenArgs -= remove;
3196 while (remove-- > 0)
3197 GenericParameters.RemoveAt (0);
3199 if (DocUtils.IsDelegate (type)) {
3200 Parameters = type.GetMethod("Invoke").Parameters;
3201 ReturnType = type.GetMethod("Invoke").ReturnType;
3202 ReturnIsReturn = true;
3206 void SetMemberInfo (MemberReference member)
3209 throw new ArgumentNullException ("member");
3210 ReturnIsReturn = true;
3214 if (member is MethodReference ) {
3215 MethodReference mr = (MethodReference) member;
3216 Parameters = mr.Parameters;
3217 if (mr.IsGenericMethod ()) {
3218 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
3221 else if (member is PropertyDefinition) {
3222 Parameters = ((PropertyDefinition) member).Parameters;
3225 if (member is MethodDefinition) {
3226 ReturnType = ((MethodDefinition) member).ReturnType;
3227 } else if (member is PropertyDefinition) {
3228 ReturnType = ((PropertyDefinition) member).PropertyType;
3229 ReturnIsReturn = false;
3232 // no remarks section for enum members
3233 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
3237 public TypeReference ReturnType;
3238 public List<GenericParameter> GenericParameters;
3239 public IList<ParameterDefinition> Parameters;
3240 public bool ReturnIsReturn;
3241 public XmlElement Node;
3242 public bool AddRemarks = true;
3243 public MemberReference Member;
3244 public TypeDefinition Type;
3246 public override string ToString ()
3248 return string.Format ("{0} - {1} - {2}", Type, Member, Node == null ? "no xml" : "with xml");
3252 class DocumentationEnumerator {
3254 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3256 return GetDocumentationTypes (assembly, forTypes, null);
3259 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3261 foreach (TypeDefinition type in assembly.GetTypes()) {
3262 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
3264 if (seen != null && seen.Contains (type.FullName))
3267 foreach (TypeDefinition nested in type.NestedTypes)
3268 yield return nested;
3272 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3274 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
3275 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
3276 oldmember.RemoveAttribute ("__monodocer-seen__");
3279 MemberReference m = GetMember (type, new DocumentationMember (oldmember));
3281 yield return new DocsNodeInfo (oldmember);
3284 yield return new DocsNodeInfo (oldmember, m);
3289 protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
3291 string membertype = member.MemberType;
3293 string returntype = member.ReturnType;
3295 string docName = member.MemberName;
3297 string[] docTypeParams = GetTypeParameters (docName, member.TypeParameters);
3299 // If we're using 'magic types', then we might get false positives ... in those cases, we keep searching
3300 MemberReference likelyCandidate = null;
3302 // Loop through all members in this type with the same name
3303 var reflectedMembers = GetReflectionMembers (type, docName).ToArray ();
3304 foreach (MemberReference mi in reflectedMembers) {
3305 bool matchedMagicType = false;
3306 if (mi is TypeDefinition) continue;
3307 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
3309 if (MDocUpdater.IsPrivate (mi))
3312 IList<ParameterDefinition> pis = null;
3313 string[] typeParams = null;
3314 if (mi is MethodDefinition) {
3315 MethodDefinition mb = (MethodDefinition) mi;
3316 pis = mb.Parameters;
3317 if (mb.IsGenericMethod ()) {
3318 IList<GenericParameter> args = mb.GenericParameters;
3319 typeParams = args.Select (p => p.Name).ToArray ();
3322 else if (mi is PropertyDefinition)
3323 pis = ((PropertyDefinition)mi).Parameters;
3325 // check type parameters
3326 int methodTcount = member.TypeParameters == null ? 0 : member.TypeParameters.Count;
3327 int reflectionTcount = typeParams == null ? 0 : typeParams.Length;
3328 if (methodTcount != reflectionTcount)
3331 // check member parameters
3332 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
3333 int pcount = pis == null ? 0 : pis.Count;
3334 if (mcount != pcount)
3337 MethodDefinition mDef = mi as MethodDefinition;
3338 if (mDef != null && !mDef.IsConstructor) {
3339 // Casting operators can overload based on return type.
3340 string rtype = GetReplacedString (
3341 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
3342 typeParams, docTypeParams);
3343 string originalRType = rtype;
3344 if (MDocUpdater.SwitchingToMagicTypes) {
3345 rtype = NativeTypeManager.ConvertFromNativeType (rtype);
3348 if ((returntype != rtype && originalRType == rtype) ||
3349 (MDocUpdater.SwitchingToMagicTypes && returntype != originalRType && returntype != rtype && originalRType != rtype)) {
3353 if (originalRType != rtype)
3354 matchedMagicType = true;
3360 for (int i = 0; i < pis.Count; i++) {
3361 string paramType = GetReplacedString (
3362 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
3363 typeParams, docTypeParams);
3365 // if magictypes, replace paramType to "classic value" ... so the comparison works
3366 string originalParamType = paramType;
3367 if (MDocUpdater.SwitchingToMagicTypes) {
3368 paramType = NativeTypeManager.ConvertFromNativeType (paramType);
3371 string xmlMemberType = member.Parameters [i];
3372 if ((!paramType.Equals(xmlMemberType) && paramType.Equals(originalParamType)) ||
3373 (MDocUpdater.SwitchingToMagicTypes && !originalParamType.Equals(xmlMemberType) && !paramType.Equals(xmlMemberType) && !paramType.Equals(originalParamType))) {
3375 // did not match ... if we're dropping the namespace, and the paramType has the dropped
3376 // namespace, we should see if it matches when added
3377 bool stillDoesntMatch = true;
3378 if (MDocUpdater.HasDroppedNamespace(type) && paramType.StartsWith (MDocUpdater.droppedNamespace)) {
3379 string withDroppedNs = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, xmlMemberType);
3381 stillDoesntMatch = withDroppedNs != paramType;
3384 if (stillDoesntMatch) {
3390 if (originalParamType != paramType)
3391 matchedMagicType = true;
3393 if (!good) continue;
3395 if (MDocUpdater.SwitchingToMagicTypes && likelyCandidate == null && matchedMagicType) {
3396 // 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
3397 likelyCandidate = mi;
3404 return likelyCandidate;
3407 static string[] GetTypeParameters (string docName, IEnumerable<string> knownParameters)
3409 if (docName [docName.Length-1] != '>')
3411 StringList types = new StringList ();
3412 int endToken = docName.Length-2;
3413 int i = docName.Length-2;
3415 if (docName [i] == ',' || docName [i] == '<') {
3416 types.Add (docName.Substring (i + 1, endToken - i));
3419 if (docName [i] == '<')
3424 var arrayTypes = types.ToArray ();
3426 if (knownParameters != null && knownParameters.Any () && arrayTypes.Length != knownParameters.Count ())
3427 return knownParameters.ToArray ();
3432 protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
3434 // In case of dropping the namespace, we have to remove the dropped NS
3435 // so that docName will match what's in the assembly/type
3436 if (MDocUpdater.HasDroppedNamespace (type) && docName.StartsWith(MDocUpdater.droppedNamespace + ".")) {
3437 int droppedNsLength = MDocUpdater.droppedNamespace.Length;
3438 docName = docName.Substring (droppedNsLength + 1, docName.Length - droppedNsLength - 1);
3441 // need to worry about 4 forms of //@MemberName values:
3442 // 1. "Normal" (non-generic) member names: GetEnumerator
3444 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
3445 // - try as-is, and try type.member (due to "kludge" for property
3447 // 3. "Normal" Generic member names: Sort<T> (CSC)
3448 // - need to remove generic parameters --> "Sort"
3449 // 4. Explicitly-implemented interface members for generic interfaces:
3450 // -- System.Collections.Generic.IEnumerable<T>.Current
3451 // - Try as-is, and try type.member, *keeping* the generic parameters.
3452 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
3453 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
3454 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
3455 // this as (1) or (2).
3456 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
3458 foreach (MemberReference mi in type.GetMembers (docName))
3460 if (CountChars (docName, '.') > 0)
3461 // might be a property; try only type.member instead of
3462 // namespace.type.member.
3463 foreach (MemberReference mi in
3464 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
3471 int startLt, startType, startMethod;
3472 startLt = startType = startMethod = -1;
3473 for (int i = 0; i < docName.Length; ++i) {
3474 switch (docName [i]) {
3483 if (numLt == 0 && (i + 1) < docName.Length)
3484 // there's another character in docName, so this <...> sequence is
3485 // probably part of a generic type -- case 4.
3489 startType = startMethod;
3495 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
3497 foreach (MemberReference mi in type.GetMembers (refName))
3501 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
3504 // If we _still_ haven't found it, we've hit another generic naming issue:
3505 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
3506 // explicitly-implemented METHOD names (not properties), e.g.
3507 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
3508 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
3509 // which the XML docs will contain.
3511 // Alas, we can't derive the Mono name from docName, so we need to iterate
3512 // over all member names, convert them into CSC format, and compare... :-(
3515 foreach (MemberReference mi in type.GetMembers ()) {
3516 if (MDocUpdater.GetMemberName (mi) == docName)
3521 static string GetReplacedString (string typeName, string[] from, string[] to)
3525 for (int i = 0; i < from.Length; ++i)
3526 typeName = typeName.Replace (from [i], to [i]);
3530 private static int CountChars (string s, char c)
3533 for (int i = 0; i < s.Length; ++i) {
3541 class EcmaDocumentationEnumerator : DocumentationEnumerator {
3546 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
3549 this.ecmadocs = ecmaDocs;
3552 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3554 HashSet<string> seen = new HashSet<string> ();
3555 return GetDocumentationTypes (assembly, forTypes, seen)
3556 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
3559 new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3562 while (ecmadocs.Read ()) {
3563 switch (ecmadocs.Name) {
3565 if (typeDepth == -1)
3566 typeDepth = ecmadocs.Depth;
3567 if (ecmadocs.NodeType != XmlNodeType.Element)
3569 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
3571 string typename = ecmadocs.GetAttribute ("FullName");
3572 string typename2 = MDocUpdater.GetTypeFileName (typename);
3573 if (forTypes != null &&
3574 forTypes.BinarySearch (typename) < 0 &&
3575 typename != typename2 &&
3576 forTypes.BinarySearch (typename2) < 0)
3579 if ((t = assembly.GetType (typename)) == null &&
3580 (t = assembly.GetType (typename2)) == null)
3582 seen.Add (typename);
3583 if (typename != typename2)
3584 seen.Add (typename2);
3585 Console.WriteLine (" Import: {0}", t.FullName);
3586 if (ecmadocs.Name != "Docs") {
3587 int depth = ecmadocs.Depth;
3588 while (ecmadocs.Read ()) {
3589 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
3593 if (!ecmadocs.IsStartElement ("Docs"))
3594 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
3604 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3606 return GetMembers (basefile, type)
3607 .Concat (base.GetDocumentationMembers (basefile, type));
3610 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
3612 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
3615 if (ecmadocs.IsEmptyElement)
3618 int membersDepth = ecmadocs.Depth;
3620 while (go && ecmadocs.Read ()) {
3621 switch (ecmadocs.Name) {
3623 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
3625 DocumentationMember dm = new DocumentationMember (ecmadocs);
3627 string xp = MDocUpdater.GetXPathForMember (dm);
3628 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
3630 if (oldmember == null) {
3631 m = GetMember (type, dm);
3633 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3634 type.FullName, dm.MemberSignatures ["C#"]);
3635 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
3638 // oldmember lookup may have failed due to type parameter renames.
3640 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
3641 if (oldmember == null) {
3642 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
3643 oldmember = basefile.CreateElement ("Member");
3644 oldmember.SetAttribute ("MemberName", dm.MemberName);
3645 members.AppendChild (oldmember);
3646 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
3647 XmlElement ms = basefile.CreateElement ("MemberSignature");
3648 ms.SetAttribute ("Language", key);
3649 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
3650 oldmember.AppendChild (ms);
3652 oldmember.SetAttribute ("__monodocer-seen__", "true");
3653 Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
3658 m = GetMember (type, new DocumentationMember (oldmember));
3660 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3661 type.FullName, dm.MemberSignatures ["C#"]);
3664 oldmember.SetAttribute ("__monodocer-seen__", "true");
3666 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
3667 if (ecmadocs.Name != "Docs")
3668 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
3673 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
3682 abstract class DocumentationImporter {
3684 public abstract void ImportDocumentation (DocsNodeInfo info);
3687 class MsxdocDocumentationImporter : DocumentationImporter {
3689 XmlDocument slashdocs;
3691 public MsxdocDocumentationImporter (string file)
3693 var xml = File.ReadAllText (file);
3695 // Ensure Unix line endings
3696 xml = xml.Replace ("\r", "");
3698 slashdocs = new XmlDocument();
3699 slashdocs.LoadXml (xml);
3702 public override void ImportDocumentation (DocsNodeInfo info)
3704 XmlNode elem = GetDocs (info.Member ?? info.Type);
3709 XmlElement e = info.Node;
3711 if (elem.SelectSingleNode("summary") != null)
3712 MDocUpdater.ClearElement(e, "summary");
3713 if (elem.SelectSingleNode("remarks") != null)
3714 MDocUpdater.ClearElement(e, "remarks");
3715 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3716 MDocUpdater.ClearElement(e, "value");
3717 MDocUpdater.ClearElement(e, "returns");
3720 foreach (XmlNode child in elem.ChildNodes) {
3721 switch (child.Name) {
3724 XmlAttribute name = child.Attributes ["name"];
3727 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3729 p2.InnerXml = child.InnerXml;
3732 // Occasionally XML documentation will use <returns/> on
3733 // properties, so let's try to normalize things.
3736 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3737 v.InnerXml = child.InnerXml;
3743 case "permission": {
3744 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3747 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3749 a = e.OwnerDocument.CreateElement (child.Name);
3750 a.SetAttribute ("cref", cref.Value);
3753 a.InnerXml = child.InnerXml;
3757 XmlAttribute cref = child.Attributes ["cref"];
3760 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3762 a = e.OwnerDocument.CreateElement ("altmember");
3763 a.SetAttribute ("cref", cref.Value);
3770 if (child.NodeType == XmlNodeType.Element &&
3771 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3774 MDocUpdater.CopyNode (child, e);
3781 private XmlNode GetDocs (MemberReference member)
3783 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3784 if (slashdocsig != null)
3785 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3790 class EcmaDocumentationImporter : DocumentationImporter {
3794 public EcmaDocumentationImporter (XmlReader ecmaDocs)
3796 this.ecmadocs = ecmaDocs;
3799 public override void ImportDocumentation (DocsNodeInfo info)
3801 if (!ecmadocs.IsStartElement ("Docs")) {
3805 XmlElement e = info.Node;
3807 int depth = ecmadocs.Depth;
3808 ecmadocs.ReadStartElement ("Docs");
3809 while (ecmadocs.Read ()) {
3810 if (ecmadocs.Name == "Docs") {
3811 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3814 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3816 if (!ecmadocs.IsStartElement ())
3818 switch (ecmadocs.Name) {
3821 string name = ecmadocs.GetAttribute ("name");
3824 XmlNode doc = e.SelectSingleNode (
3825 ecmadocs.Name + "[@name='" + name + "']");
3826 string value = ecmadocs.ReadInnerXml ();
3828 doc.InnerXml = value.Replace ("\r", "");
3835 string name = ecmadocs.Name;
3836 string cref = ecmadocs.GetAttribute ("cref");
3839 XmlNode doc = e.SelectSingleNode (
3840 ecmadocs.Name + "[@cref='" + cref + "']");
3841 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3843 doc.InnerXml = value;
3845 XmlElement n = e.OwnerDocument.CreateElement (name);
3846 n.SetAttribute ("cref", cref);
3853 string name = ecmadocs.Name;
3854 string xpath = ecmadocs.Name;
3855 StringList attributes = new StringList (ecmadocs.AttributeCount);
3856 if (ecmadocs.MoveToFirstAttribute ()) {
3858 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3859 } while (ecmadocs.MoveToNextAttribute ());
3860 ecmadocs.MoveToContent ();
3862 if (attributes.Count > 0) {
3863 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3865 XmlNode doc = e.SelectSingleNode (xpath);
3866 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3868 doc.InnerXml = value;
3871 XmlElement n = e.OwnerDocument.CreateElement (name);
3873 foreach (string a in attributes) {
3874 int eq = a.IndexOf ('=');
3875 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3886 class DocumentationMember {
3887 public StringToStringMap MemberSignatures = new StringToStringMap ();
3888 public string ReturnType;
3889 public StringList Parameters;
3890 public StringList TypeParameters;
3891 public string MemberName;
3892 public string MemberType;
3894 public DocumentationMember (XmlReader reader)
3896 MemberName = reader.GetAttribute ("MemberName");
3897 int depth = reader.Depth;
3899 StringList p = new StringList ();
3900 StringList tp = new StringList ();
3902 if (reader.NodeType != XmlNodeType.Element)
3905 bool shouldUse = true;
3907 string apistyle = reader.GetAttribute ("apistyle");
3908 shouldUse = string.IsNullOrWhiteSpace(apistyle) || apistyle == "classic"; // only use this tag if it's an 'classic' style node
3910 catch (Exception ex) {}
3911 switch (reader.Name) {
3912 case "MemberSignature":
3914 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3918 MemberType = reader.ReadElementString ();
3921 if (reader.Depth == depth + 2 && shouldUse)
3922 ReturnType = reader.ReadElementString ();
3925 if (reader.Depth == depth + 2 && shouldUse)
3926 p.Add (reader.GetAttribute ("Type"));
3928 case "TypeParameter":
3929 if (reader.Depth == depth + 2 && shouldUse)
3930 tp.Add (reader.GetAttribute ("Name"));
3933 if (reader.Depth == depth + 1)
3937 } while (go && reader.Read () && reader.Depth >= depth);
3942 TypeParameters = tp;
3944 DiscernTypeParameters ();
3948 public DocumentationMember (XmlNode node)
3950 MemberName = node.Attributes ["MemberName"].Value;
3951 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3952 XmlAttribute l = n.Attributes ["Language"];
3953 XmlAttribute v = n.Attributes ["Value"];
3954 XmlAttribute apistyle = n.Attributes ["apistyle"];
3955 bool shouldUse = apistyle == null || apistyle.Value == "classic";
3956 if (l != null && v != null && shouldUse)
3957 MemberSignatures [l.Value] = v.Value;
3959 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3960 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType[not(@apistyle) or @apistyle='classic']");
3962 ReturnType = rt.InnerText;
3963 XmlNodeList p = node.SelectNodes ("Parameters/Parameter[not(@apistyle) or @apistyle='classic']");
3965 Parameters = new StringList (p.Count);
3966 for (int i = 0; i < p.Count; ++i)
3967 Parameters.Add (p [i].Attributes ["Type"].Value);
3969 XmlNodeList tp = node.SelectNodes ("TypeParameters/TypeParameter[not(@apistyle) or @apistyle='classic']");
3971 TypeParameters = new StringList (tp.Count);
3972 for (int i = 0; i < tp.Count; ++i)
3973 TypeParameters.Add (tp [i].Attributes ["Name"].Value);
3976 DiscernTypeParameters ();
3980 void DiscernTypeParameters ()
3982 // see if we can discern the param list from the name
3983 if (MemberName.Contains ("<") && MemberName.EndsWith (">")) {
3984 var starti = MemberName.IndexOf ("<") + 1;
3985 var endi = MemberName.LastIndexOf (">");
3986 var paramlist = MemberName.Substring (starti, endi - starti);
3987 var tparams = paramlist.Split (new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
3988 TypeParameters = new StringList (tparams);
3993 public class DynamicParserContext {
3994 public ReadOnlyCollection<bool> TransformFlags;
3995 public int TransformIndex;
3997 public DynamicParserContext (ICustomAttributeProvider provider)
4000 if (provider.HasCustomAttributes &&
4001 (da = (provider.CustomAttributes.Cast<CustomAttribute>()
4002 .SingleOrDefault (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))) != null) {
4003 CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
4004 ? new CustomAttributeArgument [0]
4005 : (CustomAttributeArgument[]) da.ConstructorArguments [0].Value;
4007 TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool) t.Value).ToArray());
4012 public enum MemberFormatterState {
4014 WithinGenericTypeParameters,
4017 public abstract class MemberFormatter {
4019 public virtual string Language {
4023 public string GetName (MemberReference member)
4025 return GetName (member, null);
4028 public virtual string GetName (MemberReference member, DynamicParserContext context)
4030 TypeReference type = member as TypeReference;
4032 return GetTypeName (type, context);
4033 MethodReference method = member as MethodReference;
4034 if (method != null && method.Name == ".ctor") // method.IsConstructor
4035 return GetConstructorName (method);
4037 return GetMethodName (method);
4038 PropertyReference prop = member as PropertyReference;
4040 return GetPropertyName (prop);
4041 FieldReference field = member as FieldReference;
4043 return GetFieldName (field);
4044 EventReference e = member as EventReference;
4046 return GetEventName (e);
4047 throw new NotSupportedException ("Can't handle: " +
4048 (member == null ? "null" : member.GetType().ToString()));
4051 protected virtual string GetTypeName (TypeReference type)
4053 return GetTypeName (type, null);
4056 protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
4059 throw new ArgumentNullException ("type");
4060 return _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
4063 protected virtual char[] ArrayDelimeters {
4064 get {return new char[]{'[', ']'};}
4067 protected virtual MemberFormatterState MemberFormatterState { get; set; }
4069 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4071 if (type is ArrayType) {
4072 TypeSpecification spec = type as TypeSpecification;
4073 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
4074 return AppendArrayModifiers (buf, (ArrayType) type);
4076 if (type is ByReferenceType) {
4077 return AppendRefTypeName (buf, type, context);
4079 if (type is PointerType) {
4080 return AppendPointerTypeName (buf, type, context);
4082 if (type is GenericParameter) {
4083 return AppendTypeName (buf, type, context);
4085 AppendNamespace (buf, type);
4086 GenericInstanceType genInst = type as GenericInstanceType;
4087 if (type.GenericParameters.Count == 0 &&
4088 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
4089 return AppendFullTypeName (buf, type, context);
4091 return AppendGenericType (buf, type, context);
4094 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4096 string ns = DocUtils.GetNamespace (type);
4097 if (ns != null && ns.Length > 0)
4098 buf.Append (ns).Append ('.');
4102 protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4104 if (type.DeclaringType != null)
4105 AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
4106 return AppendTypeName (buf, type, context);
4109 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4111 if (context != null)
4112 context.TransformIndex++;
4113 return AppendTypeName (buf, type.Name);
4116 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
4118 int n = typename.IndexOf ("`");
4120 return buf.Append (typename.Substring (0, n));
4121 return buf.Append (typename);
4124 protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
4126 buf.Append (ArrayDelimeters [0]);
4127 int rank = array.Rank;
4129 buf.Append (new string (',', rank-1));
4130 return buf.Append (ArrayDelimeters [1]);
4133 protected virtual string RefTypeModifier {
4137 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4139 TypeSpecification spec = type as TypeSpecification;
4140 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4141 .Append (RefTypeModifier);
4144 protected virtual string PointerModifier {
4148 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4150 TypeSpecification spec = type as TypeSpecification;
4151 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4152 .Append (PointerModifier);
4155 protected virtual char[] GenericTypeContainer {
4156 get {return new char[]{'<', '>'};}
4159 protected virtual char NestedTypeSeparator {
4163 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4165 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4166 type is GenericInstanceType ? type.GetElementType () : type);
4167 List<TypeReference> genArgs = GetGenericArguments (type);
4170 bool insertNested = false;
4171 foreach (var decl in decls) {
4172 TypeReference declDef = decl.Resolve () ?? decl;
4174 buf.Append (NestedTypeSeparator);
4176 insertNested = true;
4177 AppendTypeName (buf, declDef, context);
4178 int ac = DocUtils.GetGenericArgumentCount (declDef);
4182 buf.Append (GenericTypeContainer [0]);
4183 var origState = MemberFormatterState;
4184 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4185 _AppendTypeName (buf, genArgs [argIdx++], context);
4186 for (int i = 1; i < c; ++i) {
4187 _AppendTypeName (buf.Append (","), genArgs [argIdx++], context);
4189 MemberFormatterState = origState;
4190 buf.Append (GenericTypeContainer [1]);
4196 protected List<TypeReference> GetGenericArguments (TypeReference type)
4198 var args = new List<TypeReference> ();
4199 GenericInstanceType inst = type as GenericInstanceType;
4201 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
4203 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
4207 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4212 protected virtual string GetConstructorName (MethodReference constructor)
4214 return constructor.Name;
4217 protected virtual string GetMethodName (MethodReference method)
4222 protected virtual string GetPropertyName (PropertyReference property)
4224 return property.Name;
4227 protected virtual string GetFieldName (FieldReference field)
4232 protected virtual string GetEventName (EventReference e)
4237 public virtual string GetDeclaration (MemberReference member)
4240 throw new ArgumentNullException ("member");
4241 TypeDefinition type = member as TypeDefinition;
4243 return GetTypeDeclaration (type);
4244 MethodDefinition method = member as MethodDefinition;
4245 if (method != null && method.IsConstructor)
4246 return GetConstructorDeclaration (method);
4248 return GetMethodDeclaration (method);
4249 PropertyDefinition prop = member as PropertyDefinition;
4251 return GetPropertyDeclaration (prop);
4252 FieldDefinition field = member as FieldDefinition;
4254 return GetFieldDeclaration (field);
4255 EventDefinition e = member as EventDefinition;
4257 return GetEventDeclaration (e);
4258 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
4261 protected virtual string GetTypeDeclaration (TypeDefinition type)
4264 throw new ArgumentNullException ("type");
4265 StringBuilder buf = new StringBuilder (type.Name.Length);
4266 _AppendTypeName (buf, type, null);
4267 AppendGenericTypeConstraints (buf, type);
4268 return buf.ToString ();
4271 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
4273 return GetConstructorName (constructor);
4276 protected virtual string GetMethodDeclaration (MethodDefinition method)
4278 if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
4279 ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
4282 // Special signature for destructors.
4283 if (method.Name == "Finalize" && method.Parameters.Count == 0)
4284 return GetFinalizerName (method);
4286 StringBuilder buf = new StringBuilder ();
4288 AppendVisibility (buf, method);
4289 if (buf.Length == 0 &&
4290 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
4293 AppendModifiers (buf, method);
4295 if (buf.Length != 0)
4297 buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
4299 AppendMethodName (buf, method);
4300 AppendGenericMethod (buf, method).Append (" ");
4301 AppendParameters (buf, method, method.Parameters);
4302 AppendGenericMethodConstraints (buf, method);
4303 return buf.ToString ();
4306 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4308 return buf.Append (method.Name);
4311 protected virtual string GetFinalizerName (MethodDefinition method)
4316 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4321 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4326 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4331 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4336 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4341 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
4343 return GetPropertyName (property);
4346 protected virtual string GetFieldDeclaration (FieldDefinition field)
4348 return GetFieldName (field);
4351 protected virtual string GetEventDeclaration (EventDefinition e)
4353 return GetEventName (e);
4357 class ILFullMemberFormatter : MemberFormatter {
4359 public override string Language {
4360 get {return "ILAsm";}
4363 protected override char NestedTypeSeparator {
4369 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4371 if (GetBuiltinType (type.FullName) != null)
4373 string ns = DocUtils.GetNamespace (type);
4374 if (ns != null && ns.Length > 0) {
4375 if (type.IsValueType)
4376 buf.Append ("valuetype ");
4378 buf.Append ("class ");
4379 buf.Append (ns).Append ('.');
4384 protected static string GetBuiltinType (string t)
4387 case "System.Byte": return "unsigned int8";
4388 case "System.SByte": return "int8";
4389 case "System.Int16": return "int16";
4390 case "System.Int32": return "int32";
4391 case "System.Int64": return "int64";
4392 case "System.IntPtr": return "native int";
4394 case "System.UInt16": return "unsigned int16";
4395 case "System.UInt32": return "unsigned int32";
4396 case "System.UInt64": return "unsigned int64";
4397 case "System.UIntPtr": return "native unsigned int";
4399 case "System.Single": return "float32";
4400 case "System.Double": return "float64";
4401 case "System.Boolean": return "bool";
4402 case "System.Char": return "char";
4403 case "System.Void": return "void";
4404 case "System.String": return "string";
4405 case "System.Object": return "object";
4410 protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
4412 return buf.Append (typename);
4415 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4417 if (type is GenericParameter) {
4418 AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
4422 string s = GetBuiltinType (type.FullName);
4424 return buf.Append (s);
4426 return base.AppendTypeName (buf, type, context);
4429 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
4431 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
4432 return buf.Append (type.Owner is TypeReference ? "!" : "!!");
4434 GenericParameterAttributes attrs = type.Attributes;
4435 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
4436 buf.Append ("class ");
4437 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
4438 buf.Append ("struct ");
4439 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
4440 buf.Append (".ctor ");
4441 IList<TypeReference> constraints = type.Constraints;
4442 MemberFormatterState = 0;
4443 if (constraints.Count > 0) {
4444 var full = new ILFullMemberFormatter ();
4445 buf.Append ("(").Append (full.GetName (constraints [0]));
4446 for (int i = 1; i < constraints.Count; ++i) {
4447 buf.Append (", ").Append (full.GetName (constraints [i]));
4451 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4453 if ((attrs & GenericParameterAttributes.Covariant) != 0)
4455 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
4460 protected override string GetTypeDeclaration (TypeDefinition type)
4462 string visibility = GetTypeVisibility (type.Attributes);
4463 if (visibility == null)
4466 StringBuilder buf = new StringBuilder ();
4468 buf.Append (".class ");
4470 buf.Append ("nested ");
4471 buf.Append (visibility).Append (" ");
4472 if (type.IsInterface)
4473 buf.Append ("interface ");
4474 if (type.IsSequentialLayout)
4475 buf.Append ("sequential ");
4476 if (type.IsAutoLayout)
4477 buf.Append ("auto ");
4478 if (type.IsAnsiClass)
4479 buf.Append ("ansi ");
4480 if (type.IsAbstract)
4481 buf.Append ("abstract ");
4482 if (type.IsSerializable)
4483 buf.Append ("serializable ");
4485 buf.Append ("sealed ");
4486 if (type.IsBeforeFieldInit)
4487 buf.Append ("beforefieldinit ");
4488 var state = MemberFormatterState;
4489 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4490 buf.Append (GetName (type));
4491 MemberFormatterState = state;
4492 var full = new ILFullMemberFormatter ();
4493 if (type.BaseType != null) {
4494 buf.Append (" extends ");
4495 if (type.BaseType.FullName == "System.Object")
4496 buf.Append ("System.Object");
4498 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
4501 foreach (var name in type.Interfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()))
4502 .Select (i => full.GetName (i))
4503 .OrderBy (n => n)) {
4505 buf.Append (" implements ");
4514 return buf.ToString ();
4517 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4519 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4520 type is GenericInstanceType ? type.GetElementType () : type);
4522 foreach (var decl in decls) {
4523 TypeReference declDef = decl.Resolve () ?? decl;
4525 buf.Append (NestedTypeSeparator);
4528 AppendTypeName (buf, declDef, context);
4532 foreach (TypeReference arg in GetGenericArguments (type)) {
4536 _AppendTypeName (buf, arg, context);
4542 static string GetTypeVisibility (TypeAttributes ta)
4544 switch (ta & TypeAttributes.VisibilityMask) {
4545 case TypeAttributes.Public:
4546 case TypeAttributes.NestedPublic:
4549 case TypeAttributes.NestedFamily:
4550 case TypeAttributes.NestedFamORAssem:
4558 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4560 return GetMethodDeclaration (constructor);
4563 protected override string GetMethodDeclaration (MethodDefinition method)
4565 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
4568 var buf = new StringBuilder ();
4569 buf.Append (".method ");
4570 AppendVisibility (buf, method);
4571 if (method.IsStatic)
4572 buf.Append ("static ");
4573 if (method.IsHideBySig)
4574 buf.Append ("hidebysig ");
4575 if (method.IsPInvokeImpl) {
4576 var info = method.PInvokeInfo;
4577 buf.Append ("pinvokeimpl (\"")
4578 .Append (info.Module.Name)
4579 .Append ("\" as \"")
4580 .Append (info.EntryPoint)
4582 if (info.IsCharSetAuto)
4583 buf.Append (" auto");
4584 if (info.IsCharSetUnicode)
4585 buf.Append (" unicode");
4586 if (info.IsCharSetAnsi)
4587 buf.Append (" ansi");
4588 if (info.IsCallConvCdecl)
4589 buf.Append (" cdecl");
4590 if (info.IsCallConvStdCall)
4591 buf.Append (" stdcall");
4592 if (info.IsCallConvWinapi)
4593 buf.Append (" winapi");
4594 if (info.IsCallConvThiscall)
4595 buf.Append (" thiscall");
4596 if (info.SupportsLastError)
4597 buf.Append (" lasterr");
4600 if (method.IsSpecialName)
4601 buf.Append ("specialname ");
4602 if (method.IsRuntimeSpecialName)
4603 buf.Append ("rtspecialname ");
4604 if (method.IsNewSlot)
4605 buf.Append ("newslot ");
4606 if (method.IsVirtual)
4607 buf.Append ("virtual ");
4608 if (!method.IsStatic)
4609 buf.Append ("instance ");
4610 _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
4612 .Append (method.Name);
4613 if (method.IsGenericMethod ()) {
4614 var state = MemberFormatterState;
4615 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4616 IList<GenericParameter> args = method.GenericParameters;
4617 if (args.Count > 0) {
4619 _AppendTypeName (buf, args [0], null);
4620 for (int i = 1; i < args.Count; ++i)
4621 _AppendTypeName (buf.Append (", "), args [i], null);
4624 MemberFormatterState = state;
4629 for (int i = 0; i < method.Parameters.Count; ++i) {
4633 _AppendTypeName (buf, method.Parameters [i].ParameterType, new DynamicParserContext (method.Parameters [i]));
4635 buf.Append (method.Parameters [i].Name);
4639 buf.Append (" cil");
4640 if (method.IsRuntime)
4641 buf.Append (" runtime");
4642 if (method.IsManaged)
4643 buf.Append (" managed");
4645 return buf.ToString ();
4648 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4650 if (DocUtils.IsExplicitlyImplemented (method)) {
4651 TypeReference iface;
4652 MethodReference ifaceMethod;
4653 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4654 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4656 .Append (ifaceMethod.Name);
4658 return base.AppendMethodName (buf, method);
4661 protected override string RefTypeModifier {
4665 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4667 if (method.IsPublic)
4668 return buf.Append ("public ");
4669 if (method.IsFamilyAndAssembly)
4670 return buf.Append ("familyandassembly");
4671 if (method.IsFamilyOrAssembly)
4672 return buf.Append ("familyorassembly");
4673 if (method.IsFamily)
4674 return buf.Append ("family");
4678 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4680 string modifiers = String.Empty;
4681 if (method.IsStatic) modifiers += " static";
4682 if (method.IsVirtual && !method.IsAbstract) {
4683 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4684 else modifiers += " override";
4686 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4687 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4688 if (method.IsFinal) modifiers += " sealed";
4689 if (modifiers == " virtual sealed") modifiers = "";
4691 return buf.Append (modifiers);
4694 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4696 if (method.IsGenericMethod ()) {
4697 IList<GenericParameter> args = method.GenericParameters;
4698 if (args.Count > 0) {
4700 buf.Append (args [0].Name);
4701 for (int i = 1; i < args.Count; ++i)
4702 buf.Append (",").Append (args [i].Name);
4709 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4711 return AppendParameters (buf, method, parameters, '(', ')');
4714 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4718 if (parameters.Count > 0) {
4719 if (DocUtils.IsExtensionMethod (method))
4720 buf.Append ("this ");
4721 AppendParameter (buf, parameters [0]);
4722 for (int i = 1; i < parameters.Count; ++i) {
4724 AppendParameter (buf, parameters [i]);
4728 return buf.Append (end);
4731 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4733 if (parameter.ParameterType is ByReferenceType) {
4734 if (parameter.IsOut)
4735 buf.Append ("out ");
4737 buf.Append ("ref ");
4739 buf.Append (GetName (parameter.ParameterType)).Append (" ");
4740 return buf.Append (parameter.Name);
4743 protected override string GetPropertyDeclaration (PropertyDefinition property)
4745 MethodDefinition gm = null, sm = null;
4747 string get_visible = null;
4748 if ((gm = property.GetMethod) != null &&
4749 (DocUtils.IsExplicitlyImplemented (gm) ||
4750 (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
4751 get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
4752 string set_visible = null;
4753 if ((sm = property.SetMethod) != null &&
4754 (DocUtils.IsExplicitlyImplemented (sm) ||
4755 (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
4756 set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
4758 if ((set_visible == null) && (get_visible == null))
4761 StringBuilder buf = new StringBuilder ()
4762 .Append (".property ");
4763 if (!(gm ?? sm).IsStatic)
4764 buf.Append ("instance ");
4765 _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
4766 buf.Append (' ').Append (property.Name);
4767 if (!property.HasParameters || property.Parameters.Count == 0)
4768 return buf.ToString ();
4772 foreach (ParameterDefinition p in property.Parameters) {
4776 _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
4780 return buf.ToString ();
4783 protected override string GetFieldDeclaration (FieldDefinition field)
4785 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4786 if (declType.IsEnum && field.Name == "value__")
4787 return null; // This member of enums aren't documented.
4789 StringBuilder buf = new StringBuilder ();
4790 AppendFieldVisibility (buf, field);
4791 if (buf.Length == 0)
4794 buf.Insert (0, ".field ");
4797 buf.Append ("static ");
4798 if (field.IsInitOnly)
4799 buf.Append ("initonly ");
4800 if (field.IsLiteral)
4801 buf.Append ("literal ");
4802 _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
4803 buf.Append (' ').Append (field.Name);
4804 AppendFieldValue (buf, field);
4806 return buf.ToString ();
4809 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4812 return buf.Append ("public ");
4813 if (field.IsFamilyAndAssembly)
4814 return buf.Append ("familyandassembly ");
4815 if (field.IsFamilyOrAssembly)
4816 return buf.Append ("familyorassembly ");
4818 return buf.Append ("family ");
4822 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4824 // enums have a value__ field, which we ignore
4825 if (field.DeclaringType.IsGenericType ())
4827 if (field.HasConstant && field.IsLiteral) {
4830 val = field.Constant;
4835 buf.Append (" = ").Append ("null");
4836 else if (val is Enum)
4838 .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4840 .Append (val.ToString ())
4842 else if (val is IFormattable) {
4843 string value = ((IFormattable)val).ToString();
4846 buf.Append ("\"" + value + "\"");
4848 buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4857 protected override string GetEventDeclaration (EventDefinition e)
4859 StringBuilder buf = new StringBuilder ();
4860 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4865 buf.Append (".event ")
4866 .Append (GetName (e.EventType))
4870 return buf.ToString ();
4874 class ILMemberFormatter : ILFullMemberFormatter {
4875 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4881 class ILNativeTypeMemberFormatter : ILFullMemberFormatter {
4882 protected static string _GetBuiltinType (string t)
4884 //string moddedType = base.GetBuiltinType (t);
4886 //return moddedType;
4890 class CSharpNativeTypeMemberFormatter : CSharpFullMemberFormatter {
4891 protected override string GetCSharpType (string t) {
4892 string moddedType = base.GetCSharpType (t);
4894 switch (moddedType) {
4895 case "int": return "nint";
4900 case "System.Drawing.SizeF":
4901 return "CoreGraphics.CGSize";
4902 case "System.Drawing.PointF":
4903 return "CoreGraphics.CGPoint";
4904 case "System.Drawing.RectangleF":
4905 return "CoreGraphics.CGPoint";
4911 class CSharpFullMemberFormatter : MemberFormatter {
4913 public override string Language {
4917 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4920 string ns = DocUtils.GetNamespace (type);
4921 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
4922 buf.Append (ns).Append ('.');
4926 protected virtual string GetCSharpType (string t)
4929 case "System.Byte": return "byte";
4930 case "System.SByte": return "sbyte";
4931 case "System.Int16": return "short";
4932 case "System.Int32": return "int";
4933 case "System.Int64": return "long";
4935 case "System.UInt16": return "ushort";
4936 case "System.UInt32": return "uint";
4937 case "System.UInt64": return "ulong";
4939 case "System.Single": return "float";
4940 case "System.Double": return "double";
4941 case "System.Decimal": return "decimal";
4942 case "System.Boolean": return "bool";
4943 case "System.Char": return "char";
4944 case "System.Void": return "void";
4945 case "System.String": return "string";
4946 case "System.Object": return "object";
4951 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4953 if (context != null && context.TransformFlags != null &&
4954 (context.TransformFlags.Count == 0 || context.TransformFlags [context.TransformIndex])) {
4955 context.TransformIndex++;
4956 return buf.Append ("dynamic");
4959 if (type is GenericParameter)
4960 return AppendGenericParameterConstraints (buf, (GenericParameter) type, context).Append (type.Name);
4961 string t = type.FullName;
4962 if (!t.StartsWith ("System.")) {
4963 return base.AppendTypeName (buf, type, context);
4966 string s = GetCSharpType (t);
4968 if (context != null)
4969 context.TransformIndex++;
4970 return buf.Append (s);
4973 return base.AppendTypeName (buf, type, context);
4976 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
4978 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
4980 GenericParameterAttributes attrs = type.Attributes;
4981 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
4982 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
4986 buf.Append ("out ");
4990 protected override string GetTypeDeclaration (TypeDefinition type)
4992 string visibility = GetTypeVisibility (type.Attributes);
4993 if (visibility == null)
4996 StringBuilder buf = new StringBuilder ();
4998 buf.Append (visibility);
5001 MemberFormatter full = new CSharpFullMemberFormatter ();
5003 if (DocUtils.IsDelegate (type)) {
5004 buf.Append("delegate ");
5005 MethodDefinition invoke = type.GetMethod ("Invoke");
5006 buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
5007 buf.Append (GetName (type));
5008 AppendParameters (buf, invoke, invoke.Parameters);
5009 AppendGenericTypeConstraints (buf, type);
5012 return buf.ToString();
5015 if (type.IsAbstract && !type.IsInterface)
5016 buf.Append("abstract ");
5017 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
5018 buf.Append("sealed ");
5019 buf.Replace ("abstract sealed", "static");
5021 buf.Append (GetTypeKind (type));
5023 buf.Append (GetCSharpType (type.FullName) == null
5028 TypeReference basetype = type.BaseType;
5029 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
5032 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
5033 .Select (iface => full.GetName (iface))
5037 if (basetype != null || interface_names.Count > 0)
5040 if (basetype != null) {
5041 buf.Append (full.GetName (basetype));
5042 if (interface_names.Count > 0)
5046 for (int i = 0; i < interface_names.Count; i++){
5049 buf.Append (interface_names [i]);
5051 AppendGenericTypeConstraints (buf, type);
5054 return buf.ToString ();
5057 static string GetTypeKind (TypeDefinition t)
5063 if (t.IsClass || t.FullName == "System.Enum")
5067 throw new ArgumentException(t.FullName);
5070 static string GetTypeVisibility (TypeAttributes ta)
5072 switch (ta & TypeAttributes.VisibilityMask) {
5073 case TypeAttributes.Public:
5074 case TypeAttributes.NestedPublic:
5077 case TypeAttributes.NestedFamily:
5078 case TypeAttributes.NestedFamORAssem:
5086 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
5088 if (type.GenericParameters.Count == 0)
5090 return AppendConstraints (buf, type.GenericParameters);
5093 private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
5095 foreach (GenericParameter genArg in genArgs) {
5096 GenericParameterAttributes attrs = genArg.Attributes;
5097 IList<TypeReference> constraints = genArg.Constraints;
5098 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
5101 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
5102 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
5103 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
5106 if (!isref && !isvt && !isnew && constraints.Count == 0)
5108 buf.Append (" where ").Append (genArg.Name).Append (" : ");
5110 buf.Append ("class");
5114 buf.Append ("struct");
5117 if (constraints.Count > 0 && !isvt) {
5120 buf.Append (GetTypeName (constraints [0]));
5121 for (int i = 1; i < constraints.Count; ++i)
5122 buf.Append (", ").Append (GetTypeName (constraints [i]));
5124 if (isnew && !isvt) {
5127 buf.Append ("new()");
5133 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5135 StringBuilder buf = new StringBuilder ();
5136 AppendVisibility (buf, constructor);
5137 if (buf.Length == 0)
5141 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
5142 AppendParameters (buf, constructor, constructor.Parameters);
5145 return buf.ToString ();
5148 protected override string GetMethodDeclaration (MethodDefinition method)
5150 string decl = base.GetMethodDeclaration (method);
5156 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
5158 if (DocUtils.IsExplicitlyImplemented (method)) {
5159 TypeReference iface;
5160 MethodReference ifaceMethod;
5161 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5162 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
5164 .Append (ifaceMethod.Name);
5166 return base.AppendMethodName (buf, method);
5169 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
5171 if (method.GenericParameters.Count == 0)
5173 return AppendConstraints (buf, method.GenericParameters);
5176 protected override string RefTypeModifier {
5180 protected override string GetFinalizerName (MethodDefinition method)
5182 return "~" + method.DeclaringType.Name + " ()";
5185 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
5189 if (method.IsPublic)
5190 return buf.Append ("public");
5191 if (method.IsFamily || method.IsFamilyOrAssembly)
5192 return buf.Append ("protected");
5196 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
5198 string modifiers = String.Empty;
5199 if (method.IsStatic) modifiers += " static";
5200 if (method.IsVirtual && !method.IsAbstract) {
5201 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
5202 else modifiers += " override";
5204 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
5205 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
5206 if (method.IsFinal) modifiers += " sealed";
5207 if (modifiers == " virtual sealed") modifiers = "";
5209 return buf.Append (modifiers);
5212 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
5214 if (method.IsGenericMethod ()) {
5215 IList<GenericParameter> args = method.GenericParameters;
5216 if (args.Count > 0) {
5218 buf.Append (args [0].Name);
5219 for (int i = 1; i < args.Count; ++i)
5220 buf.Append (",").Append (args [i].Name);
5227 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
5229 return AppendParameters (buf, method, parameters, '(', ')');
5232 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
5236 if (parameters.Count > 0) {
5237 if (DocUtils.IsExtensionMethod (method))
5238 buf.Append ("this ");
5239 AppendParameter (buf, parameters [0]);
5240 for (int i = 1; i < parameters.Count; ++i) {
5242 AppendParameter (buf, parameters [i]);
5246 return buf.Append (end);
5249 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
5251 if (parameter.ParameterType is ByReferenceType) {
5252 if (parameter.IsOut)
5253 buf.Append ("out ");
5255 buf.Append ("ref ");
5257 buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
5258 buf.Append (parameter.Name);
5259 if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant) {
5260 buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
5265 protected override string GetPropertyDeclaration (PropertyDefinition property)
5267 MethodDefinition method;
5269 string get_visible = null;
5270 if ((method = property.GetMethod) != null &&
5271 (DocUtils.IsExplicitlyImplemented (method) ||
5272 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5273 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5274 string set_visible = null;
5275 if ((method = property.SetMethod) != null &&
5276 (DocUtils.IsExplicitlyImplemented (method) ||
5277 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5278 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5280 if ((set_visible == null) && (get_visible == null))
5284 StringBuilder buf = new StringBuilder ();
5285 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
5286 buf.Append (visibility = get_visible);
5287 else if (set_visible != null && get_visible == null)
5288 buf.Append (visibility = set_visible);
5290 buf.Append (visibility = "public");
5292 // Pick an accessor to use for static/virtual/override/etc. checks.
5293 method = property.SetMethod;
5295 method = property.GetMethod;
5297 string modifiers = String.Empty;
5298 if (method.IsStatic) modifiers += " static";
5299 if (method.IsVirtual && !method.IsAbstract) {
5300 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
5301 modifiers += " virtual";
5303 modifiers += " override";
5305 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
5306 if (method.IsAbstract && !declDef.IsInterface)
5307 modifiers += " abstract";
5309 modifiers += " sealed";
5310 if (modifiers == " virtual sealed")
5312 buf.Append (modifiers).Append (' ');
5314 buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
5316 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
5317 string name = property.Name;
5318 foreach (MemberReference mi in defs) {
5319 if (mi == property) {
5324 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
5326 if (property.Parameters.Count != 0) {
5327 AppendParameters (buf, method, property.Parameters, '[', ']');
5331 if (get_visible != null) {
5332 if (get_visible != visibility)
5333 buf.Append (' ').Append (get_visible);
5334 buf.Append (" get;");
5336 if (set_visible != null) {
5337 if (set_visible != visibility)
5338 buf.Append (' ').Append (set_visible);
5339 buf.Append (" set;");
5343 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
5346 protected override string GetFieldDeclaration (FieldDefinition field)
5348 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
5349 if (declType.IsEnum && field.Name == "value__")
5350 return null; // This member of enums aren't documented.
5352 StringBuilder buf = new StringBuilder ();
5353 AppendFieldVisibility (buf, field);
5354 if (buf.Length == 0)
5357 if (declType.IsEnum)
5360 if (field.IsStatic && !field.IsLiteral)
5361 buf.Append (" static");
5362 if (field.IsInitOnly)
5363 buf.Append (" readonly");
5364 if (field.IsLiteral)
5365 buf.Append (" const");
5367 buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
5368 buf.Append (field.Name);
5369 AppendFieldValue (buf, field);
5372 return buf.ToString ();
5375 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
5378 return buf.Append ("public");
5379 if (field.IsFamily || field.IsFamilyOrAssembly)
5380 return buf.Append ("protected");
5384 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
5386 // enums have a value__ field, which we ignore
5387 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
5388 field.DeclaringType.IsGenericType ())
5390 if (field.HasConstant && field.IsLiteral) {
5393 val = field.Constant;
5398 buf.Append (" = ").Append ("null");
5399 else if (val is Enum)
5400 buf.Append (" = ").Append (val.ToString ());
5401 else if (val is IFormattable) {
5402 string value = ((IFormattable)val).ToString();
5404 value = "\"" + value + "\"";
5405 buf.Append (" = ").Append (value);
5411 protected override string GetEventDeclaration (EventDefinition e)
5413 StringBuilder buf = new StringBuilder ();
5414 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
5418 AppendModifiers (buf, e.AddMethod);
5420 buf.Append (" event ");
5421 buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters [0]))).Append (' ');
5422 buf.Append (e.Name).Append (';');
5424 return buf.ToString ();
5428 class CSharpMemberFormatter : CSharpFullMemberFormatter {
5429 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5435 class DocTypeFullMemberFormatter : MemberFormatter {
5436 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
5438 protected override char NestedTypeSeparator {
5443 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
5444 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5450 class SlashDocMemberFormatter : MemberFormatter {
5452 protected override char[] GenericTypeContainer {
5453 get {return new char[]{'{', '}'};}
5456 private bool AddTypeCount = true;
5458 private TypeReference genDeclType;
5459 private MethodReference genDeclMethod;
5461 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
5463 if (type is GenericParameter) {
5465 if (genDeclType != null) {
5466 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
5467 for (int i = 0; i < genArgs.Count; ++i) {
5468 if (genArgs [i].Name == type.Name) {
5469 buf.Append ('`').Append (i);
5474 if (genDeclMethod != null) {
5475 IList<GenericParameter> genArgs = null;
5476 if (genDeclMethod.IsGenericMethod ()) {
5477 genArgs = genDeclMethod.GenericParameters;
5478 for (int i = 0; i < genArgs.Count; ++i) {
5479 if (genArgs [i].Name == type.Name) {
5480 buf.Append ("``").Append (i);
5486 if (genDeclType == null && genDeclMethod == null) {
5487 // Probably from within an explicitly implemented interface member,
5488 // where CSC uses parameter names instead of indices (why?), e.g.
5489 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
5490 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
5491 buf.Append (type.Name);
5493 if (buf.Length == l) {
5494 throw new Exception (string.Format (
5495 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
5496 type.Name, genDeclType, genDeclMethod));
5500 base.AppendTypeName (buf, type, context);
5502 int numArgs = type.GenericParameters.Count;
5503 if (type.DeclaringType != null)
5504 numArgs -= type.GenericParameters.Count;
5506 buf.Append ('`').Append (numArgs);
5513 protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
5515 buf.Append (ArrayDelimeters [0]);
5516 int rank = array.Rank;
5519 for (int i = 1; i < rank; ++i) {
5523 return buf.Append (ArrayDelimeters [1]);
5526 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5529 base.AppendGenericType (buf, type, context);
5531 AppendType (buf, type, context);
5535 private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5537 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
5538 bool insertNested = false;
5539 int prevParamCount = 0;
5540 foreach (var decl in decls) {
5542 buf.Append (NestedTypeSeparator);
5543 insertNested = true;
5544 base.AppendTypeName (buf, decl, context);
5545 int argCount = DocUtils.GetGenericArgumentCount (decl);
5546 int numArgs = argCount - prevParamCount;
5547 prevParamCount = argCount;
5549 buf.Append ('`').Append (numArgs);
5554 public override string GetDeclaration (MemberReference member)
5556 TypeReference r = member as TypeReference;
5558 return "T:" + GetTypeName (r);
5560 return base.GetDeclaration (member);
5563 protected override string GetConstructorName (MethodReference constructor)
5565 return GetMethodDefinitionName (constructor, "#ctor");
5568 protected override string GetMethodName (MethodReference method)
5571 MethodDefinition methodDef = method as MethodDefinition;
5572 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
5575 TypeReference iface;
5576 MethodReference ifaceMethod;
5577 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
5578 AddTypeCount = false;
5579 name = GetTypeName (iface) + "." + ifaceMethod.Name;
5580 AddTypeCount = true;
5582 return GetMethodDefinitionName (method, name);
5585 private string GetMethodDefinitionName (MethodReference method, string name)
5587 StringBuilder buf = new StringBuilder ();
5588 buf.Append (GetTypeName (method.DeclaringType));
5590 buf.Append (name.Replace (".", "#"));
5591 if (method.IsGenericMethod ()) {
5592 IList<GenericParameter> genArgs = method.GenericParameters;
5593 if (genArgs.Count > 0)
5594 buf.Append ("``").Append (genArgs.Count);
5596 IList<ParameterDefinition> parameters = method.Parameters;
5598 genDeclType = method.DeclaringType;
5599 genDeclMethod = method;
5600 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
5604 genDeclMethod = null;
5606 return buf.ToString ();
5609 private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
5611 if (parameters.Count == 0)
5616 AppendParameter (buf, genArgs, parameters [0]);
5617 for (int i = 1; i < parameters.Count; ++i) {
5619 AppendParameter (buf, genArgs, parameters [i]);
5622 return buf.Append (')');
5625 private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
5627 AddTypeCount = false;
5628 buf.Append (GetTypeName (parameter.ParameterType));
5629 AddTypeCount = true;
5633 protected override string GetPropertyName (PropertyReference property)
5637 PropertyDefinition propertyDef = property as PropertyDefinition;
5638 MethodDefinition method = null;
5639 if (propertyDef != null)
5640 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
5641 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
5642 name = property.Name;
5644 TypeReference iface;
5645 MethodReference ifaceMethod;
5646 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5647 AddTypeCount = false;
5648 name = string.Join ("#", new string[]{
5649 GetTypeName (iface).Replace (".", "#"),
5650 DocUtils.GetMember (property.Name)
5652 AddTypeCount = true;
5655 StringBuilder buf = new StringBuilder ();
5656 buf.Append (GetName (property.DeclaringType));
5659 IList<ParameterDefinition> parameters = property.Parameters;
5660 if (parameters.Count > 0) {
5661 genDeclType = property.DeclaringType;
5663 IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
5664 AppendParameter (buf, genArgs, parameters [0]);
5665 for (int i = 1; i < parameters.Count; ++i) {
5667 AppendParameter (buf, genArgs, parameters [i]);
5672 return buf.ToString ();
5675 protected override string GetFieldName (FieldReference field)
5677 return string.Format ("{0}.{1}",
5678 GetName (field.DeclaringType), field.Name);
5681 protected override string GetEventName (EventReference e)
5683 return string.Format ("{0}.{1}",
5684 GetName (e.DeclaringType), e.Name);
5687 protected override string GetTypeDeclaration (TypeDefinition type)
5689 string name = GetName (type);
5695 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5697 string name = GetName (constructor);
5703 protected override string GetMethodDeclaration (MethodDefinition method)
5705 string name = GetName (method);
5708 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
5709 genDeclType = method.DeclaringType;
5710 genDeclMethod = method;
5711 name += "~" + GetName (method.ReturnType);
5713 genDeclMethod = null;
5718 protected override string GetPropertyDeclaration (PropertyDefinition property)
5720 string name = GetName (property);
5726 protected override string GetFieldDeclaration (FieldDefinition field)
5728 string name = GetName (field);
5734 protected override string GetEventDeclaration (EventDefinition e)
5736 string name = GetName (e);
5743 class FileNameMemberFormatter : SlashDocMemberFormatter {
5744 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5749 protected override char NestedTypeSeparator {
5754 class ResolvedTypeInfo {
5755 TypeDefinition typeDef;
5757 public ResolvedTypeInfo (TypeReference value) {
5761 public TypeReference Reference { get; private set; }
5763 public TypeDefinition Definition {
5765 if (typeDef == null) {
5766 typeDef = Reference.Resolve ();
5773 /// <summary>Formats attribute values. Should return true if it is able to format the value.</summary>
5774 class AttributeValueFormatter {
5775 public virtual bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5777 TypeReference valueType = type.Reference;
5779 returnvalue = "null";
5782 if (valueType.FullName == "System.Type") {
5783 var vTypeRef = v as TypeReference;
5784 if (vTypeRef != null)
5785 returnvalue = "typeof(" + NativeTypeManager.GetTranslatedName (vTypeRef) + ")"; // TODO: drop NS handling
5787 returnvalue = "typeof(" + v.ToString () + ")";
5791 if (valueType.FullName == "System.String") {
5792 returnvalue = "\"" + v.ToString () + "\"";
5795 if (valueType.FullName == "System.Char") {
5796 returnvalue = "'" + v.ToString () + "'";
5800 returnvalue = (bool)v ? "true" : "false";
5804 TypeDefinition valueDef = type.Definition;
5805 if (valueDef == null || !valueDef.IsEnum) {
5806 returnvalue = v.ToString ();
5810 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5811 var values = MDocUpdater.GetEnumerationValues (valueDef);
5812 long c = MDocUpdater.ToInt64 (v);
5813 if (values.ContainsKey (c)) {
5814 returnvalue = typename + "." + values [c];
5823 /// <summary>The final value formatter in the pipeline ... if no other formatter formats the value,
5824 /// then this one will serve as the default implementation.</summary>
5825 class DefaultAttributeValueFormatter : AttributeValueFormatter {
5826 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5828 returnvalue = "(" + MDocUpdater.GetDocTypeFullName (type.Reference) + ") " + v.ToString ();
5833 /// <summary>Flags enum formatter that assumes powers of two values.</summary>
5834 /// <remarks>As described here: https://msdn.microsoft.com/en-us/library/vstudio/ms229062(v=vs.100).aspx</remarks>
5835 class StandardFlagsEnumFormatter : AttributeValueFormatter {
5836 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5838 TypeReference valueType = type.Reference;
5839 TypeDefinition valueDef = type.Definition;
5840 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
5842 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5843 var values = MDocUpdater.GetEnumerationValues (valueDef);
5844 long c = MDocUpdater.ToInt64 (v);
5845 returnvalue = string.Join (" | ",
5846 (from i in values.Keys
5847 where (c & i) == i && i != 0
5848 select typename + "." + values [i])
5849 .DefaultIfEmpty (c.ToString ()).ToArray ());
5859 /// <summary>A custom formatter for the ObjCRuntime.Platform enumeration.</summary>
5860 class ApplePlatformEnumFormatter : AttributeValueFormatter {
5861 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5863 TypeReference valueType = type.Reference;
5864 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5865 TypeDefinition valueDef = type.Definition;
5866 if (typename.Contains ("ObjCRuntime.Platform") && valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
5868 var values = MDocUpdater.GetEnumerationValues (valueDef);
5869 long c = MDocUpdater.ToInt64 (v);
5871 returnvalue = Format (c, values, typename);
5879 string Format (long c, IDictionary<long, string> values, string typename)
5881 int iosarch, iosmajor, iosminor, iossubminor;
5882 int macarch, macmajor, macminor, macsubminor;
5883 GetEncodingiOS (c, out iosarch, out iosmajor, out iosminor, out iossubminor);
5884 GetEncodingMac ((ulong)c, out macarch, out macmajor, out macminor, out macsubminor);
5886 if (iosmajor == 0 & iosminor == 0 && iossubminor == 0) {
5887 return FormatValues ("Mac", macarch, macmajor, macminor, macsubminor);
5890 if (macmajor == 0 & macminor == 0 && macsubminor == 0) {
5891 return FormatValues ("iOS", iosarch, iosmajor, iosminor, iossubminor);
5894 return string.Format ("(Platform){0}", c);
5897 string FormatValues (string plat, int arch, int major, int minor, int subminor)
5899 string archstring = "";
5908 return string.Format ("Platform.{4}_{0}_{1}{2} | Platform.{4}_Arch{3}",
5911 subminor == 0 ? "" : "_" + subminor.ToString (),
5917 void GetEncodingiOS (long entireLong, out int archindex, out int major, out int minor, out int subminor)
5919 long lowerBits = entireLong & 0xffffffff;
5920 int lowerBitsAsInt = (int) lowerBits;
5921 GetEncoding (lowerBitsAsInt, out archindex, out major, out minor, out subminor);
5924 void GetEncodingMac (ulong entireLong, out int archindex, out int major, out int minor, out int subminor)
5926 ulong higherBits = entireLong & 0xffffffff00000000;
5927 int higherBitsAsInt = (int) ((higherBits) >> 32);
5928 GetEncoding (higherBitsAsInt, out archindex, out major, out minor, out subminor);
5931 void GetEncoding (Int32 encodedBits, out int archindex, out int major, out int minor, out int subminor)
5933 // format is AAJJNNSS
5934 archindex = (int)((encodedBits & 0xFF000000) >> 24);
5935 major = (int)((encodedBits & 0x00FF0000) >> 16);
5936 minor = (int)((encodedBits & 0x0000FF00) >> 8);
5937 subminor = (int)((encodedBits & 0x000000FF) >> 0);