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);
682 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
685 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
691 public void DoUpdateNS (string ns, string nspath, string outpath)
693 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
694 AssemblyDefinition assembly = assemblies [0];
696 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
697 XmlDocument basefile = new XmlDocument();
698 string typefile = Path.Combine(nspath, file.Name);
700 basefile.Load(typefile);
701 } catch (Exception e) {
702 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
706 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
707 TypeDefinition type = assembly.GetType(typename);
710 if (!string.IsNullOrWhiteSpace (droppedNamespace)) {
711 string nameWithNs = string.Format ("{0}.{1}", droppedNamespace, typename);
712 type = assembly.GetType (nameWithNs);
714 Warning ("Type no longer in assembly: " + typename);
721 seenTypes[type] = seenTypes;
722 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false);
725 // Stub types not in the directory
726 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
727 if (type.Namespace != ns || seenTypes.ContainsKey(type))
730 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"));
731 if (td == null) continue;
735 private static string GetTypeFileName (TypeReference type)
737 return filenameFormatter.GetName (type);
740 public static string GetTypeFileName (string typename)
742 StringBuilder filename = new StringBuilder (typename.Length);
746 for (int i = 0; i < typename.Length; ++i) {
747 char c = typename [i];
756 filename.Append ('`').Append ((numArgs+1).ToString());
771 return filename.ToString ();
774 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
776 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
777 index_assembly.SetAttribute ("Name", assembly.Name.Name);
778 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
780 AssemblyNameDefinition name = assembly.Name;
781 if (name.HasPublicKey) {
782 XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
783 var key = new StringBuilder (name.PublicKey.Length*3 + 2);
785 foreach (byte b in name.PublicKey)
786 key.AppendFormat ("{0,2:x2} ", b);
788 pubkey.InnerText = key.ToString ();
789 index_assembly.AppendChild (pubkey);
792 if (!string.IsNullOrEmpty (name.Culture)) {
793 XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
794 culture.InnerText = name.Culture;
795 index_assembly.AppendChild (culture);
798 MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
799 parent.AppendChild(index_assembly);
802 private void AddIndexType (TypeDefinition type, XmlElement index_types)
804 string typename = GetTypeFileName(type);
806 // Add namespace and type nodes into the index file as needed
807 string ns = DocUtils.GetNamespace (type);
808 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode ("Namespace[@Name='" + ns + "']");
809 if (nsnode == null) {
810 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
811 nsnode.SetAttribute ("Name", ns);
812 index_types.AppendChild (nsnode);
814 string doc_typename = GetDocTypeName (type);
815 XmlElement typenode = (XmlElement) nsnode.SelectSingleNode ("Type[@Name='" + typename + "']");
816 if (typenode == null) {
817 typenode = index_types.OwnerDocument.CreateElement ("Type");
818 typenode.SetAttribute ("Name", typename);
819 nsnode.AppendChild (typenode);
821 if (typename != doc_typename)
822 typenode.SetAttribute("DisplayName", doc_typename);
824 typenode.RemoveAttribute("DisplayName");
826 typenode.SetAttribute ("Kind", GetTypeKind (type));
829 private void DoUpdateAssemblies (string source, string dest)
831 string indexfile = dest + "/index.xml";
833 if (System.IO.File.Exists(indexfile)) {
834 index = new XmlDocument();
835 index.Load(indexfile);
838 ClearElement(index.DocumentElement, "Assembly");
839 ClearElement(index.DocumentElement, "Attributes");
841 index = CreateIndexStub();
844 string defaultTitle = "Untitled";
845 if (assemblies.Count == 1)
846 defaultTitle = assemblies[0].Name.Name;
847 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
849 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
850 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
851 index_assemblies.RemoveAll ();
854 HashSet<string> goodfiles = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
856 foreach (AssemblyDefinition assm in assemblies) {
857 AddIndexAssembly (assm, index_assemblies);
858 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
861 SortIndexEntries (index_types);
863 CleanupFiles (dest, goodfiles);
864 CleanupIndexTypes (index_types, goodfiles);
865 CleanupExtensions (index_types);
867 WriteFile (indexfile, FileMode.Create,
868 writer => WriteXml(index.DocumentElement, writer));
871 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
873 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
875 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
876 string typename = GetTypeFileName(type);
877 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0 || forwardedTypes.Contains (type.FullName))
880 string reltypepath = DoUpdateType (type, source, dest);
881 if (reltypepath == null)
884 // Add namespace and type nodes into the index file as needed
885 AddIndexType (type, index_types);
887 // Ensure the namespace index file exists
888 string namespaceToUse = type.Namespace;
889 if (HasDroppedNamespace(assembly)) {
890 namespaceToUse = string.Format ("{0}.{1}", droppedNamespace, namespaceToUse);
892 string onsdoc = DocUtils.PathCombine (dest, namespaceToUse + ".xml");
893 string nsdoc = DocUtils.PathCombine (dest, "ns-" + namespaceToUse + ".xml");
894 if (File.Exists (onsdoc)) {
895 File.Move (onsdoc, nsdoc);
898 if (!File.Exists (nsdoc)) {
899 Console.WriteLine("New Namespace File: " + type.Namespace);
900 WriteNamespaceStub(namespaceToUse, dest);
903 goodfiles.Add (reltypepath);
907 private static void SortIndexEntries (XmlElement indexTypes)
909 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
910 XmlNodeComparer c = new AttributeNameComparer ();
911 SortXmlNodes (indexTypes, namespaces, c);
913 for (int i = 0; i < namespaces.Count; ++i)
914 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
917 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
919 MyXmlNodeList l = new MyXmlNodeList (children.Count);
920 for (int i = 0; i < children.Count; ++i)
921 l.Add (children [i]);
923 for (int i = l.Count - 1; i > 0; --i) {
924 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
928 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
930 public abstract int Compare (XmlNode x, XmlNode y);
932 public int Compare (object x, object y)
934 return Compare ((XmlNode) x, (XmlNode) y);
938 class AttributeNameComparer : XmlNodeComparer {
941 public AttributeNameComparer ()
946 public AttributeNameComparer (string attribute)
948 this.attribute = attribute;
951 public override int Compare (XmlNode x, XmlNode y)
953 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
957 class VersionComparer : XmlNodeComparer {
958 public override int Compare (XmlNode x, XmlNode y)
960 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
961 string a = GetVersion (x.InnerText);
962 string b = GetVersion (y.InnerText);
963 return new Version (a).CompareTo (new Version (b));
966 static string GetVersion (string v)
968 int n = v.IndexOf ("x");
971 return v.Substring (0, n-1);
975 private static string GetTypeKind (TypeDefinition type)
978 return "Enumeration";
979 if (type.IsValueType)
981 if (type.IsInterface)
983 if (DocUtils.IsDelegate (type))
985 if (type.IsClass || type.FullName == "System.Enum") // FIXME
987 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
990 public static bool IsPublic (TypeDefinition type)
992 TypeDefinition decl = type;
993 while (decl != null) {
994 if (!(decl.IsPublic || decl.IsNestedPublic ||
995 decl.IsNestedFamily || decl.IsNestedFamily || decl.IsNestedFamilyOrAssembly)) {
998 decl = (TypeDefinition) decl.DeclaringType;
1003 private void CleanupFiles (string dest, HashSet<string> goodfiles)
1005 // Look for files that no longer correspond to types
1006 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
1007 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
1008 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
1009 if (!goodfiles.Contains (relTypeFile)) {
1010 XmlDocument doc = new XmlDocument ();
1011 doc.Load (typefile.FullName);
1012 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
1013 string assemblyName = doc.SelectSingleNode ("/Type/AssemblyInfo/AssemblyName").InnerText;
1014 AssemblyDefinition assembly = assemblies.FirstOrDefault (a => a.Name.Name == assemblyName);
1016 Action saveDoc = () => {
1017 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
1018 WriteXml(doc.DocumentElement, writer);
1021 if (e != null && !no_assembly_versions && assembly != null && assemblyName != null && UpdateAssemblyVersions (e, assembly, GetAssemblyVersions(assemblyName), false)) {
1023 goodfiles.Add (relTypeFile);
1027 Action actuallyDelete = () => {
1028 string newname = typefile.FullName + ".remove";
1029 try { System.IO.File.Delete (newname); } catch (Exception) { Warning ("Unable to delete existing file: {0}", newname); }
1030 try { typefile.MoveTo (newname); } catch (Exception) { Warning ("Unable to rename to: {0}", newname); }
1031 Console.WriteLine ("Class no longer present; file renamed: " + Path.Combine (nsdir.Name, typefile.Name));
1034 if (string.IsNullOrWhiteSpace (PreserveTag)) { // only do this if there was not a -preserve
1037 var unifiedAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[@apistyle='unified']");
1038 var classicAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[@apistyle='classic']");
1039 var unifiedMembers = doc.SelectNodes ("//Member[@apistyle='unified']|//Member/AssemblyInfo[@apistyle='unified']");
1040 var classicMembers = doc.SelectNodes ("//Member[@apistyle='classic']|//Member/AssemblyInfo[@apistyle='classic']");
1041 bool isUnifiedRun = HasDroppedAnyNamespace ();
1042 bool isClassicOrNormalRun = !isUnifiedRun;
1044 Action<XmlNode, ApiStyle> removeStyles = (x, style) => {
1045 var styledNodes = doc.SelectNodes("//*[@apistyle='"+ style.ToString ().ToLowerInvariant () +"']");
1046 if (styledNodes != null && styledNodes.Count > 0) {
1047 foreach(var node in styledNodes.Cast<XmlNode> ()) {
1048 node.ParentNode.RemoveChild (node);
1053 if (isClassicOrNormalRun) {
1054 if (unifiedAssemblyNode != null || unifiedMembers.Count > 0) {
1055 Warning ("*** this type is marked as unified, not deleting during this run: {0}", typefile.FullName);
1056 // if truly removed from both assemblies, it will be removed fully during the unified run
1057 removeStyles (doc, ApiStyle.Classic);
1060 // we should be safe to delete here because it was not marked as a unified assembly
1065 if (classicAssemblyNode != null || classicMembers.Count > 0) {
1066 Warning ("*** this type is marked as classic, not deleting {0}", typefile.FullName);
1069 // safe to delete because it wasn't marked as a classic assembly, so the type is gone in both.
1079 private static TextWriter OpenWrite (string path, FileMode mode)
1081 var w = new StreamWriter (
1082 new FileStream (path, mode),
1083 new UTF8Encoding (false)
1089 private string[] GetAssemblyVersions (string assemblyName)
1091 return (from a in assemblies
1092 where a.Name.Name == assemblyName
1093 select GetAssemblyVersion (a)).ToArray ();
1096 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
1098 // Look for type nodes that no longer correspond to types
1099 MyXmlNodeList remove = new MyXmlNodeList ();
1100 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
1101 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
1102 if (!goodfiles.Contains (fulltypename)) {
1103 remove.Add (typenode);
1106 foreach (XmlNode n in remove)
1107 n.ParentNode.RemoveChild (n);
1110 private void CleanupExtensions (XmlElement index_types)
1112 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
1113 if (extensionMethods.Count == 0) {
1116 index_types.SelectSingleNode ("/Overview").RemoveChild (e);
1120 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
1121 index_types.SelectSingleNode ("/Overview").AppendChild (e);
1125 extensionMethods.Sort (DefaultExtensionMethodComparer);
1126 foreach (XmlNode m in extensionMethods) {
1127 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
1131 class ExtensionMethodComparer : XmlNodeComparer {
1132 public override int Compare (XmlNode x, XmlNode y)
1134 XmlNode xLink = x.SelectSingleNode ("Member/Link");
1135 XmlNode yLink = y.SelectSingleNode ("Member/Link");
1137 int n = xLink.Attributes ["Type"].Value.CompareTo (
1138 yLink.Attributes ["Type"].Value);
1141 n = xLink.Attributes ["Member"].Value.CompareTo (
1142 yLink.Attributes ["Member"].Value);
1143 if (n == 0 && !object.ReferenceEquals (x, y))
1144 throw new InvalidOperationException ("Duplicate extension method found!");
1149 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
1151 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince)
1153 Console.WriteLine(message + ": " + type.FullName);
1155 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
1157 // Update type metadata
1158 UpdateType(basefile.DocumentElement, type);
1160 // Update existing members. Delete member nodes that no longer should be there,
1161 // and remember what members are already documented so we don't add them again.
1163 MyXmlNodeList todelete = new MyXmlNodeList ();
1165 foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
1166 XmlElement oldmember = info.Node;
1167 MemberReference oldmember2 = info.Member;
1168 string sig = oldmember2 != null ? memberFormatters [0].GetDeclaration (oldmember2) : null;
1170 // Interface implementations and overrides are deleted from the docs
1171 // unless the overrides option is given.
1172 if (oldmember2 != null && sig == null)
1175 // Deleted (or signature changed)
1176 if (oldmember2 == null) {
1177 if (!no_assembly_versions && UpdateAssemblyVersions (oldmember, type.Module.Assembly, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
1180 DeleteMember ("Member Removed", output, oldmember, todelete, type);
1185 if (seenmembers.ContainsKey (sig)) {
1186 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
1187 // ignore, already seen
1189 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
1190 DeleteMember ("Duplicate Member Found", output, oldmember, todelete, type);
1192 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
1196 // Update signature information
1199 // get all apistyles of sig from info.Node
1200 var styles = oldmember.GetElementsByTagName ("MemberSignature").Cast<XmlElement> ()
1201 .Where (x => x.GetAttribute ("Language") == "C#" && !seenmembers.ContainsKey(x.GetAttribute("Value")))
1202 .Select (x => x.GetAttribute ("Value"));
1204 foreach (var stylesig in styles) {
1205 seenmembers.Add (stylesig, oldmember);
1208 foreach (XmlElement oldmember in todelete)
1209 oldmember.ParentNode.RemoveChild (oldmember);
1212 if (!DocUtils.IsDelegate (type)) {
1213 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
1214 var typemembers = type.GetMembers()
1216 if (m is TypeDefinition) return false;
1217 string sig = memberFormatters [0].GetDeclaration (m);
1218 if (sig == null) return false;
1219 if (seenmembers.ContainsKey(sig)) return false;
1221 // Verify that the member isn't an explicitly implemented
1222 // member of an internal interface, in which case we shouldn't return true.
1223 MethodDefinition methdef = null;
1224 if (m is MethodDefinition)
1225 methdef = m as MethodDefinition;
1226 else if (m is PropertyDefinition) {
1227 var prop = m as PropertyDefinition;
1228 methdef = prop.GetMethod ?? prop.SetMethod;
1231 if (methdef != null) {
1232 TypeReference iface;
1233 MethodReference imethod;
1235 if (methdef.Overrides.Count == 1) {
1236 DocUtils.GetInfoForExplicitlyImplementedMethod (methdef, out iface, out imethod);
1237 if (!IsPublic (iface.Resolve ())) return false;
1244 foreach (MemberReference m in typemembers) {
1245 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
1246 if (mm == null) continue;
1248 if (MDocUpdater.SwitchingToMagicTypes) {
1249 // this is a unified style API that obviously doesn't exist in the classic API. Let's mark
1250 // it with apistyle="unified", so that it's not displayed for classic style APIs
1251 mm.SetAttribute ("apistyle", "unified");
1254 members.AppendChild( mm );
1256 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
1261 // Import code snippets from files
1262 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
1263 if (!(code is XmlElement)) continue;
1264 string file = ((XmlElement)code).GetAttribute("src");
1265 string lang = ((XmlElement)code).GetAttribute("lang");
1267 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
1269 code.InnerText = src;
1273 if (insertSince && since != null) {
1274 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
1275 docs.AppendChild (CreateSinceNode (basefile));
1279 XmlElement d = basefile.DocumentElement ["Docs"];
1280 XmlElement m = basefile.DocumentElement ["Members"];
1281 if (d != null && m != null)
1282 basefile.DocumentElement.InsertBefore (
1283 basefile.DocumentElement.RemoveChild (d), m);
1284 SortTypeMembers (m);
1288 WriteXml(basefile.DocumentElement, Console.Out);
1290 FileInfo file = new FileInfo (output);
1291 if (!file.Directory.Exists) {
1292 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
1293 file.Directory.Create ();
1295 WriteFile (output, FileMode.Create,
1296 writer => WriteXml(basefile.DocumentElement, writer));
1300 private string GetCodeSource (string lang, string file)
1303 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
1304 // Grab the specified region
1305 string region = "#region " + file.Substring (anchorStart + 4);
1306 file = file.Substring (0, anchorStart + 3);
1308 using (StreamReader reader = new StreamReader (file)) {
1310 StringBuilder src = new StringBuilder ();
1312 while ((line = reader.ReadLine ()) != null) {
1313 if (line.Trim() == region) {
1314 indent = line.IndexOf (region);
1317 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
1322 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
1325 return src.ToString ();
1327 } catch (Exception e) {
1328 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
1329 file, region, show_exceptions ? e.ToString () : e.Message);
1334 using (StreamReader reader = new StreamReader (file))
1335 return reader.ReadToEnd ();
1336 } catch (Exception e) {
1337 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
1342 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete, TypeDefinition type)
1344 string format = output != null
1345 ? "{0}: File='{1}'; Signature='{4}'"
1346 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1347 string signature = member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value;
1351 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1352 member.Attributes ["MemberName"].Value,
1355 // Identify all of the different states that could affect our decision to delete the member
1356 bool shouldPreserve = !string.IsNullOrWhiteSpace (PreserveTag);
1357 bool hasContent = MemberDocsHaveUserContent (member);
1358 bool shouldDelete = !shouldPreserve && (delete || !hasContent);
1360 bool unifiedRun = HasDroppedNamespace (type);
1362 var classicAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[@apistyle='classic']");
1363 bool nodeIsClassic = classicAssemblyInfo != null || member.HasApiStyle (ApiStyle.Classic);
1364 var unifiedAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']");
1365 bool nodeIsUnified = unifiedAssemblyInfo != null || member.HasApiStyle (ApiStyle.Unified);
1367 Action actuallyDelete = () => {
1368 todelete.Add (member);
1372 if (!shouldDelete) {
1373 // explicitly not deleting
1374 string message = shouldPreserve ?
1375 "Not deleting '{0}' due to --preserve." :
1376 "Not deleting '{0}'; must be enabled with the --delete option";
1377 Warning (message, signature);
1378 } else if (unifiedRun && nodeIsClassic) {
1379 // this is a unified run, and the member doesn't exist, but is marked as being in the classic assembly.
1380 member.RemoveApiStyle (ApiStyle.Unified);
1381 Warning ("Not removing '{0}' since it's still in the classic assembly.", signature);
1382 } else if (unifiedRun && !nodeIsClassic) {
1383 // unified run, and the node is not classic, which means it doesn't exist anywhere.
1386 if (!nodeIsClassic && !nodeIsUnified) { // regular codepath (ie. not classic/unified)
1388 } else { // this is a classic run
1389 Warning ("Removing classic from '{0}' ... will be removed in the unified run if not present there.", signature);
1390 member.RemoveApiStyle (ApiStyle.Classic);
1391 if (classicAssemblyInfo != null) {
1392 member.RemoveChild (classicAssemblyInfo);
1398 class MemberComparer : XmlNodeComparer {
1399 public override int Compare (XmlNode x, XmlNode y)
1402 string xMemberName = x.Attributes ["MemberName"].Value;
1403 string yMemberName = y.Attributes ["MemberName"].Value;
1405 // generic methods *end* with '>'
1406 // it's possible for explicitly implemented generic interfaces to
1407 // contain <...> without being a generic method
1408 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1409 (r = xMemberName.CompareTo (yMemberName)) != 0)
1413 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1414 xMemberName = xMemberName.Substring (0, lt);
1415 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1416 yMemberName = yMemberName.Substring (0, lt);
1417 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1420 // if @MemberName matches, then it's either two different types of
1421 // members sharing the same name, e.g. field & property, or it's an
1422 // overloaded method.
1423 // for different type, sort based on MemberType value.
1424 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1425 y.SelectSingleNode ("MemberType").InnerText);
1429 // same type -- must be an overloaded method. Sort based on type
1430 // parameter count, then parameter count, then by the parameter
1432 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1433 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1434 if (xTypeParams.Count != yTypeParams.Count)
1435 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1436 for (int i = 0; i < xTypeParams.Count; ++i) {
1437 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1438 yTypeParams [i].Attributes ["Name"].Value);
1443 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1444 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1445 if (xParams.Count != yParams.Count)
1446 return xParams.Count <= yParams.Count ? -1 : 1;
1447 for (int i = 0; i < xParams.Count; ++i) {
1448 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1449 yParams [i].Attributes ["Type"].Value);
1453 // all parameters match, but return value might not match if it was
1454 // changed between one version and another.
1455 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1456 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1457 if (xReturn != null && yReturn != null) {
1458 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1467 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1469 private static void SortTypeMembers (XmlNode members)
1471 if (members == null)
1473 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1476 private static bool MemberDocsHaveUserContent (XmlNode e)
1478 e = (XmlElement)e.SelectSingleNode("Docs");
1479 if (e == null) return false;
1480 foreach (XmlElement d in e.SelectNodes("*"))
1481 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1486 // UPDATE HELPER FUNCTIONS
1488 // CREATE A STUB DOCUMENTATION FILE
1490 public XmlElement StubType (TypeDefinition type, string output)
1492 string typesig = typeFormatters [0].GetDeclaration (type);
1493 if (typesig == null) return null; // not publicly visible
1495 XmlDocument doc = new XmlDocument();
1496 XmlElement root = doc.CreateElement("Type");
1497 doc.AppendChild (root);
1499 DoUpdateType2 ("New Type", doc, type, output, true);
1504 private XmlElement CreateSinceNode (XmlDocument doc)
1506 XmlElement s = doc.CreateElement ("since");
1507 s.SetAttribute ("version", since);
1511 // STUBBING/UPDATING FUNCTIONS
1513 public void UpdateType (XmlElement root, TypeDefinition type)
1515 root.SetAttribute("Name", GetDocTypeName (type));
1516 root.SetAttribute("FullName", GetDocTypeFullName (type));
1518 foreach (MemberFormatter f in typeFormatters) {
1519 string element = "TypeSignature[@Language='" + f.Language + "']";
1520 string valueToUse = f.GetDeclaration (type);
1523 root.SelectNodes (element).Cast<XmlElement> ().ToArray (),
1524 x => x.GetAttribute ("Value") == valueToUse,
1525 x => x.SetAttribute ("Value", valueToUse),
1527 var node = WriteElementAttribute (root, element, "Language", f.Language, forceNewElement: true);
1528 var newnode = WriteElementAttribute (root, node, "Value", valueToUse);
1534 string assemblyInfoNodeFilter = MDocUpdater.HasDroppedNamespace (type) ? "[@apistyle='unified']" : "[not(@apistyle) or @apistyle='classic']";
1537 root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ().ToArray (),
1538 x => x.SelectSingleNode("AssemblyName").InnerText == type.Module.Assembly.Name.Name,
1539 x => WriteElementText(x, "AssemblyName", type.Module.Assembly.Name.Name),
1541 XmlElement ass = WriteElement(root, "AssemblyInfo", forceNewElement:true);
1543 if (MDocUpdater.HasDroppedNamespace (type)) ass.SetAttribute ("apistyle", "unified");
1551 foreach(var ass in root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ())
1553 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1554 if (!no_assembly_versions) {
1555 UpdateAssemblyVersions (root, type, true);
1558 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1559 foreach (var version in versions)
1560 ass.RemoveChild (version);
1562 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1563 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1565 ClearElement(ass, "AssemblyCulture");
1568 // Why-oh-why do we put assembly attributes in each type file?
1569 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1570 // since they're outdated in current docs, and a waste of space.
1571 //MakeAttributes(ass, type.Assembly, true);
1572 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1573 if (assattrs != null)
1574 ass.RemoveChild(assattrs);
1576 NormalizeWhitespace(ass);
1579 if (type.IsGenericType ()) {
1580 MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
1582 ClearElement(root, "TypeParameters");
1585 if (type.BaseType != null) {
1586 XmlElement basenode = WriteElement(root, "Base");
1588 string basetypename = GetDocTypeFullName (type.BaseType);
1589 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1590 WriteElementText(root, "Base/BaseTypeName", basetypename);
1592 // Document how this type instantiates the generic parameters of its base type
1593 TypeReference origBase = type.BaseType.GetElementType ();
1594 if (origBase.IsGenericType ()) {
1595 ClearElement(basenode, "BaseTypeArguments");
1596 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1597 IList<TypeReference> baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1598 IList<GenericParameter> baseGenParams = origBase.GenericParameters;
1599 if (baseGenArgs.Count != baseGenParams.Count)
1600 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1601 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1602 GenericParameter param = baseGenParams [i];
1603 TypeReference value = baseGenArgs [i];
1605 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1606 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1607 bta.AppendChild(arg);
1608 arg.SetAttribute ("TypeParamName", param.Name);
1609 arg.InnerText = GetDocTypeFullName (value);
1613 ClearElement(root, "Base");
1616 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1617 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1618 List<string> interface_names = userInterfaces
1619 .Select (iface => GetDocTypeFullName (iface))
1623 XmlElement interfaces = WriteElement(root, "Interfaces");
1624 interfaces.RemoveAll();
1625 foreach (string iname in interface_names) {
1626 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1627 interfaces.AppendChild(iface);
1628 WriteElementText(iface, "InterfaceName", iname);
1631 ClearElement(root, "Interfaces");
1634 MakeAttributes (root, GetCustomAttributes (type), type);
1636 if (DocUtils.IsDelegate (type)) {
1637 MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
1638 var member = type.GetMethod ("Invoke");
1639 MakeParameters(root, member, member.Parameters);
1640 MakeReturnValue(root, member);
1643 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1644 MakeDocNode (typeInfo);
1646 if (!DocUtils.IsDelegate (type))
1647 WriteElement (root, "Members");
1649 OrderTypeNodes (root, root.ChildNodes);
1650 NormalizeWhitespace(root);
1653 static readonly string[] TypeNodeOrder = {
1657 "ThreadingSafetyStatement",
1658 "ThreadSafetyStatement",
1670 static void OrderTypeNodes (XmlNode member, XmlNodeList children)
1672 ReorderNodes (member, children, TypeNodeOrder);
1675 internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1677 List<T> l = new List<T> (list);
1682 private void UpdateMember (DocsNodeInfo info)
1684 XmlElement me = (XmlElement) info.Node;
1685 MemberReference mi = info.Member;
1687 foreach (MemberFormatter f in memberFormatters) {
1688 string element = "MemberSignature[@Language='" + f.Language + "']";
1690 var valueToUse = f.GetDeclaration (mi);
1693 me.SelectNodes (element).Cast<XmlElement> ().ToArray(),
1694 x => x.GetAttribute("Value") == valueToUse,
1695 x => x.SetAttribute ("Value", valueToUse),
1697 var node = WriteElementAttribute (me, element, "Language", f.Language, forceNewElement:true);
1698 var newNode = WriteElementAttribute (me, node, "Value", valueToUse);
1705 WriteElementText(me, "MemberType", GetMemberType(mi));
1707 if (!no_assembly_versions) {
1708 UpdateAssemblyVersions (me, mi, true);
1711 ClearElement (me, "AssemblyInfo");
1714 MakeAttributes (me, GetCustomAttributes (mi), mi.DeclaringType);
1716 MakeReturnValue(me, mi, MDocUpdater.HasDroppedNamespace(mi));
1717 if (mi is MethodReference) {
1718 MethodReference mb = (MethodReference) mi;
1719 if (mb.IsGenericMethod ())
1720 MakeTypeParameters (me, mb.GenericParameters, mi, MDocUpdater.HasDroppedNamespace(mi));
1722 MakeParameters(me, mi, MDocUpdater.HasDroppedNamespace(mi));
1725 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1726 WriteElementText(me, "MemberValue", fieldValue);
1728 info.Node = WriteElement (me, "Docs");
1730 OrderMemberNodes (me, me.ChildNodes);
1731 UpdateExtensionMethods (me, info);
1734 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, MemberReference member) {
1735 AddXmlNode (relevant, valueMatches, setValue, makeNewNode, member.Module);
1738 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, TypeDefinition type) {
1739 AddXmlNode (relevant, valueMatches, setValue, makeNewNode, type.Module);
1742 /// <summary>Adds an xml node, reusing the node if it's available</summary>
1743 /// <param name="relevant">The existing set of nodes</param>
1744 /// <param name="valueMatches">Checks to see if the node's value matches what you're trying to write.</param>
1745 /// <param name="setValue">Sets the node's value</param>
1746 /// <param name="makeNewNode">Creates a new node, if valueMatches returns false.</param>
1747 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, ModuleDefinition module)
1749 bool shouldDuplicate = MDocUpdater.HasDroppedNamespace (module);
1750 var styleToUse = shouldDuplicate ? ApiStyle.Unified : ApiStyle.Classic;
1751 var existing = relevant;
1753 bool addedOldApiStyle = false;
1755 if (shouldDuplicate) {
1756 existing = existing.Where (n => n.HasApiStyle (styleToUse)).ToArray ();
1757 foreach (var n in relevant.Where (n => n.DoesNotHaveApiStyle (styleToUse))) {
1758 if (valueMatches (n)) {
1762 n.AddApiStyle (ApiStyle.Classic);
1763 addedOldApiStyle = true;
1768 if (!existing.Any ()) {
1769 var newNode = makeNewNode ();
1770 if (shouldDuplicate && addedOldApiStyle) {
1771 newNode.AddApiStyle (ApiStyle.Unified);
1775 var itemToReuse = existing.First ();
1776 setValue (itemToReuse);
1778 if (shouldDuplicate && addedOldApiStyle) {
1779 itemToReuse.AddApiStyle (styleToUse);
1785 static readonly string[] MemberNodeOrder = {
1800 static void OrderMemberNodes (XmlNode member, XmlNodeList children)
1802 ReorderNodes (member, children, MemberNodeOrder);
1805 static void ReorderNodes (XmlNode node, XmlNodeList children, string[] ordering)
1807 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1808 for (int i = 0; i < ordering.Length; ++i) {
1809 for (int j = 0; j < children.Count; ++j) {
1810 XmlNode c = children [j];
1811 if (c.Name == ordering [i]) {
1812 newChildren.Add (c);
1816 if (newChildren.Count >= 0)
1817 node.PrependChild ((XmlNode) newChildren [0]);
1818 for (int i = 1; i < newChildren.Count; ++i) {
1819 XmlNode prev = (XmlNode) newChildren [i-1];
1820 XmlNode cur = (XmlNode) newChildren [i];
1821 node.RemoveChild (cur);
1822 node.InsertAfter (cur, prev);
1826 IEnumerable<string> GetCustomAttributes (MemberReference mi)
1828 IEnumerable<string> attrs = Enumerable.Empty<string>();
1830 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1832 attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1834 PropertyDefinition pd = mi as PropertyDefinition;
1836 if (pd.GetMethod != null)
1837 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1838 if (pd.SetMethod != null)
1839 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1842 EventDefinition ed = mi as EventDefinition;
1844 if (ed.AddMethod != null)
1845 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1846 if (ed.RemoveMethod != null)
1847 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1853 IEnumerable<string> GetCustomAttributes (IList<CustomAttribute> attributes, string prefix)
1855 foreach (CustomAttribute attribute in attributes.OrderBy (ca => ca.AttributeType.FullName)) {
1857 TypeDefinition attrType = attribute.AttributeType as TypeDefinition;
1858 if (attrType != null && !IsPublic (attrType))
1860 if (slashdocFormatter.GetName (attribute.AttributeType) == null)
1863 if (Array.IndexOf (IgnorableAttributes, attribute.AttributeType.FullName) >= 0)
1866 StringList fields = new StringList ();
1868 for (int i = 0; i < attribute.ConstructorArguments.Count; ++i) {
1869 CustomAttributeArgument argument = attribute.ConstructorArguments [i];
1870 fields.Add (MakeAttributesValueString (
1875 (from namedArg in attribute.Fields
1876 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value })
1878 (from namedArg in attribute.Properties
1879 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value }))
1880 .OrderBy (v => v.Name);
1881 foreach (var d in namedArgs)
1882 fields.Add (string.Format ("{0}={1}", d.Name,
1883 MakeAttributesValueString (d.Value, d.Type)));
1885 string a2 = String.Join(", ", fields.ToArray ());
1886 if (a2 != "") a2 = "(" + a2 + ")";
1888 string name = attribute.GetDeclaringType();
1889 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1890 yield return prefix + name + a2;
1894 static readonly string[] ValidExtensionMembers = {
1903 static readonly string[] ValidExtensionDocMembers = {
1909 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1911 MethodDefinition me = info.Member as MethodDefinition;
1914 if (info.Parameters.Count < 1)
1916 if (!DocUtils.IsExtensionMethod (me))
1919 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1920 XmlNode member = e.CloneNode (true);
1921 em.AppendChild (member);
1922 RemoveExcept (member, ValidExtensionMembers);
1923 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1924 WriteElementText (member, "MemberType", "ExtensionMethod");
1925 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1926 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1927 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1928 member.AppendChild (link);
1929 AddTargets (em, info);
1931 extensionMethods.Add (em);
1934 private static void RemoveExcept (XmlNode node, string[] except)
1938 MyXmlNodeList remove = null;
1939 foreach (XmlNode n in node.ChildNodes) {
1940 if (Array.BinarySearch (except, n.Name) < 0) {
1942 remove = new MyXmlNodeList ();
1947 foreach (XmlNode n in remove)
1948 node.RemoveChild (n);
1951 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1953 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1954 member.PrependChild (targets);
1955 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1956 AppendElementAttributeText (targets, "Target", "Type",
1957 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1960 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1961 IList<TypeReference> constraints = gp.Constraints;
1962 if (constraints.Count == 0)
1963 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1965 foreach (TypeReference c in constraints)
1966 AppendElementAttributeText(targets, "Target", "Type",
1967 slashdocFormatter.GetDeclaration (c));
1971 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1974 TypeDefinition type = field.DeclaringType.Resolve ();
1975 if (type != null && type.IsEnum) return false;
1977 if (type != null && type.IsGenericType ()) return false;
1978 if (!field.HasConstant)
1980 if (field.IsLiteral) {
1981 object val = field.Constant;
1982 if (val == null) value = "null";
1983 else if (val is Enum) value = val.ToString();
1984 else if (val is IFormattable) {
1985 value = ((IFormattable)val).ToString();
1987 value = "\"" + value + "\"";
1989 if (value != null && value != "")
1995 // XML HELPER FUNCTIONS
1997 internal static XmlElement WriteElement(XmlNode parent, string element, bool forceNewElement = false) {
1998 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1999 if (ret == null || forceNewElement) {
2000 string[] path = element.Split('/');
2001 foreach (string p in path) {
2002 ret = (XmlElement)parent.SelectSingleNode(p);
2003 if (ret == null || forceNewElement) {
2005 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
2006 ename = ename.Substring(0, ename.IndexOf('['));
2007 ret = parent.OwnerDocument.CreateElement(ename);
2008 parent.AppendChild(ret);
2017 private static XmlElement WriteElementText(XmlNode parent, string element, string value, bool forceNewElement = false) {
2018 XmlElement node = WriteElement(parent, element, forceNewElement: forceNewElement);
2019 node.InnerText = value;
2023 static XmlElement AppendElementText (XmlNode parent, string element, string value)
2025 XmlElement n = parent.OwnerDocument.CreateElement (element);
2026 parent.AppendChild (n);
2027 n.InnerText = value;
2031 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
2033 XmlElement n = parent.OwnerDocument.CreateElement (element);
2034 parent.AppendChild (n);
2035 n.SetAttribute (attribute, value);
2039 internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
2041 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
2042 dest.AppendChild (copy);
2046 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
2047 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
2050 node = WriteElement(parent, element);
2051 node.InnerText = value;
2053 private static XmlElement WriteElementAttribute(XmlElement parent, string element, string attribute, string value, bool forceNewElement = false) {
2054 XmlElement node = WriteElement(parent, element, forceNewElement:forceNewElement);
2055 return WriteElementAttribute (parent, node, attribute, value);
2057 private static XmlElement WriteElementAttribute(XmlElement parent, XmlElement node, string attribute, string value) {
2058 if (node.GetAttribute (attribute) != value) {
2059 node.SetAttribute (attribute, value);
2063 internal static void ClearElement(XmlElement parent, string name) {
2064 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
2066 parent.RemoveChild(node);
2069 // DOCUMENTATION HELPER FUNCTIONS
2071 private void MakeDocNode (DocsNodeInfo info)
2073 List<GenericParameter> genericParams = info.GenericParameters;
2074 IList<ParameterDefinition> parameters = info.Parameters;
2075 TypeReference returntype = info.ReturnType;
2076 bool returnisreturn = info.ReturnIsReturn;
2077 XmlElement e = info.Node;
2078 bool addremarks = info.AddRemarks;
2080 WriteElementInitialText(e, "summary", "To be added.");
2082 if (parameters != null) {
2083 string[] values = new string [parameters.Count];
2084 for (int i = 0; i < values.Length; ++i)
2085 values [i] = parameters [i].Name;
2086 UpdateParameters (e, "param", values);
2089 if (genericParams != null) {
2090 string[] values = new string [genericParams.Count];
2091 for (int i = 0; i < values.Length; ++i)
2092 values [i] = genericParams [i].Name;
2093 UpdateParameters (e, "typeparam", values);
2096 string retnodename = null;
2097 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
2098 retnodename = returnisreturn ? "returns" : "value";
2099 string retnodename_other = !returnisreturn ? "returns" : "value";
2101 // If it has a returns node instead of a value node, change its name.
2102 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
2103 if (retother != null) {
2104 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
2105 foreach (XmlNode node in retother)
2106 retnode.AppendChild(node.CloneNode(true));
2107 e.ReplaceChild(retnode, retother);
2109 WriteElementInitialText(e, retnodename, "To be added.");
2112 ClearElement(e, "returns");
2113 ClearElement(e, "value");
2117 WriteElementInitialText(e, "remarks", "To be added.");
2119 if (exceptions.HasValue && info.Member != null &&
2120 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
2121 UpdateExceptions (e, info.Member);
2124 foreach (DocumentationImporter importer in importers)
2125 importer.ImportDocumentation (info);
2127 OrderDocsNodes (e, e.ChildNodes);
2128 NormalizeWhitespace(e);
2131 static readonly string[] DocsNodeOrder = {
2132 "typeparam", "param", "summary", "returns", "value", "remarks",
2135 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
2137 ReorderNodes (docs, children, DocsNodeOrder);
2141 private void UpdateParameters (XmlElement e, string element, string[] values)
2143 if (values != null) {
2144 XmlNode[] paramnodes = new XmlNode[values.Length];
2146 // Some documentation had param nodes with leading spaces.
2147 foreach (XmlElement paramnode in e.SelectNodes(element)){
2148 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
2151 // If a member has only one parameter, we can track changes to
2152 // the name of the parameter easily.
2153 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
2154 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
2157 bool reinsert = false;
2159 // Pick out existing and still-valid param nodes, and
2160 // create nodes for parameters not in the file.
2161 Hashtable seenParams = new Hashtable();
2162 for (int pi = 0; pi < values.Length; pi++) {
2163 string p = values [pi];
2166 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
2167 if (paramnodes[pi] != null) continue;
2169 XmlElement pe = e.OwnerDocument.CreateElement(element);
2170 pe.SetAttribute("name", p);
2171 pe.InnerText = "To be added.";
2172 paramnodes[pi] = pe;
2176 // Remove parameters that no longer exist and check all params are in the right order.
2178 MyXmlNodeList todelete = new MyXmlNodeList ();
2179 foreach (XmlElement paramnode in e.SelectNodes(element)) {
2180 string name = paramnode.GetAttribute("name");
2181 if (!seenParams.ContainsKey(name)) {
2182 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2183 Warning ("The following param node can only be deleted if the --delete option is given: ");
2184 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
2186 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
2187 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2191 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
2192 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2193 e.ParentNode.Attributes ["MemberName"].Value,
2196 Warning ("\tValue={0}", paramnode.OuterXml);
2198 todelete.Add (paramnode);
2203 if ((int)seenParams[name] != idx)
2209 foreach (XmlNode n in todelete) {
2210 n.ParentNode.RemoveChild (n);
2213 // Re-insert the parameter nodes at the top of the doc section.
2215 for (int pi = values.Length-1; pi >= 0; pi--)
2216 e.PrependChild(paramnodes[pi]);
2218 // Clear all existing param nodes
2219 foreach (XmlNode paramnode in e.SelectNodes(element)) {
2220 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2221 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
2222 Console.WriteLine(paramnode.OuterXml);
2224 paramnode.ParentNode.RemoveChild(paramnode);
2230 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
2232 string existingName = pe.GetAttribute ("name");
2233 pe.SetAttribute ("name", newName);
2234 if (existingName == newName)
2236 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
2237 if (paramref.GetAttribute ("name").Trim () == existingName)
2238 paramref.SetAttribute ("name", newName);
2241 class CrefComparer : XmlNodeComparer {
2243 public CrefComparer ()
2247 public override int Compare (XmlNode x, XmlNode y)
2249 string xType = x.Attributes ["cref"].Value;
2250 string yType = y.Attributes ["cref"].Value;
2251 string xNamespace = GetNamespace (xType);
2252 string yNamespace = GetNamespace (yType);
2254 int c = xNamespace.CompareTo (yNamespace);
2257 return xType.CompareTo (yType);
2260 static string GetNamespace (string type)
2262 int n = type.LastIndexOf ('.');
2264 return type.Substring (0, n);
2265 return string.Empty;
2269 private void UpdateExceptions (XmlNode docs, MemberReference member)
2271 string indent = new string (' ', 10);
2272 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
2273 string cref = slashdocFormatter.GetDeclaration (source.Exception);
2274 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
2277 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
2278 e.SetAttribute ("cref", cref);
2279 e.InnerXml = "To be added; from:\n" + indent + "<see cref=\"" +
2280 string.Join ("\" />,\n" + indent + "<see cref=\"",
2281 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
2282 .OrderBy (s => s)) +
2284 docs.AppendChild (e);
2286 SortXmlNodes (docs, docs.SelectNodes ("exception"),
2287 new CrefComparer ());
2290 private static void NormalizeWhitespace(XmlElement e) {
2291 // Remove all text and whitespace nodes from the element so it
2292 // is outputted with nice indentation and no blank lines.
2293 ArrayList deleteNodes = new ArrayList();
2294 foreach (XmlNode n in e)
2295 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2297 foreach (XmlNode n in deleteNodes)
2298 n.ParentNode.RemoveChild(n);
2301 private static bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
2303 TypeDefinition type = member as TypeDefinition;
2305 type = member.DeclaringType as TypeDefinition;
2306 return UpdateAssemblyVersions(root, type.Module.Assembly, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
2309 private static string GetAssemblyVersion (AssemblyDefinition assembly)
2311 return assembly.Name.Version.ToString();
2314 private static bool UpdateAssemblyVersions(XmlElement root, AssemblyDefinition assembly, string[] assemblyVersions, bool add)
2316 XmlElement av = (XmlElement) root.SelectSingleNode ("AssemblyVersions");
2318 // AssemblyVersions is not part of the spec
2319 root.RemoveChild (av);
2322 string oldNodeFilter = "AssemblyInfo[not(@apistyle) or @apistyle='classic']";
2323 string newNodeFilter = "AssemblyInfo[@apistyle='unified']";
2324 string thisNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? newNodeFilter : oldNodeFilter;
2325 string thatNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? oldNodeFilter : newNodeFilter;
2327 XmlElement e = (XmlElement) root.SelectSingleNode (thisNodeFilter);
2329 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2331 if (MDocUpdater.HasDroppedNamespace (assembly)) {
2332 e.SetAttribute ("apistyle", "unified");
2335 root.AppendChild(e);
2338 var thatNode = (XmlElement) root.SelectSingleNode (thatNodeFilter);
2339 if (MDocUpdater.HasDroppedNamespace (assembly) && thatNode != null) {
2340 // there's a classic node, we should add apistyles
2341 e.SetAttribute ("apistyle", "unified");
2342 thatNode.SetAttribute ("apistyle", "classic");
2345 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
2346 .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
2348 // matches.Count > 0 && add: ignore -- already present
2349 if (matches.Count > 0 && !add) {
2350 foreach (XmlNode c in matches)
2353 else if (matches.Count == 0 && add) {
2354 foreach (string sv in assemblyVersions) {
2355 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2361 // matches.Count == 0 && !add: ignore -- already not present
2362 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2363 SortXmlNodes (e, avs, new VersionComparer ());
2365 bool anyNodesLeft = avs.Count != 0;
2366 if (!anyNodesLeft) {
2367 e.ParentNode.RemoveChild (e);
2369 return anyNodesLeft;
2372 // FIXME: get TypeReferences instead of string comparison?
2373 private static string[] IgnorableAttributes = {
2374 // Security related attributes
2375 "System.Reflection.AssemblyKeyFileAttribute",
2376 "System.Reflection.AssemblyDelaySignAttribute",
2377 // Present in @RefType
2378 "System.Runtime.InteropServices.OutAttribute",
2379 // For naming the indexer to use when not using indexers
2380 "System.Reflection.DefaultMemberAttribute",
2381 // for decimal constants
2382 "System.Runtime.CompilerServices.DecimalConstantAttribute",
2383 // compiler generated code
2384 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
2385 // more compiler generated code, e.g. iterator methods
2386 "System.Diagnostics.DebuggerHiddenAttribute",
2387 "System.Runtime.CompilerServices.FixedBufferAttribute",
2388 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
2389 // extension methods
2390 "System.Runtime.CompilerServices.ExtensionAttribute",
2391 // Used to differentiate 'object' from C#4 'dynamic'
2392 "System.Runtime.CompilerServices.DynamicAttribute",
2395 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes, TypeReference t=null)
2397 if (!attributes.Any ()) {
2398 ClearElement (root, "Attributes");
2402 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2406 e = root.OwnerDocument.CreateElement("Attributes");
2408 foreach (string attribute in attributes) {
2409 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2412 WriteElementText(ae, "AttributeName", attribute);
2415 if (e.ParentNode == null)
2416 root.AppendChild(e);
2418 NormalizeWhitespace(e);
2421 public static string MakeAttributesValueString (object v, TypeReference valueType)
2423 var formatters = new [] {
2424 new AttributeValueFormatter (),
2425 new ApplePlatformEnumFormatter (),
2426 new StandardFlagsEnumFormatter (),
2427 new DefaultAttributeValueFormatter (),
2430 ResolvedTypeInfo type = new ResolvedTypeInfo (valueType);
2431 foreach (var formatter in formatters) {
2432 string formattedValue;
2433 if (formatter.TryFormatValue (v, type, out formattedValue)) {
2434 return formattedValue;
2438 // this should never occur because the DefaultAttributeValueFormatter will always
2439 // successfully format the value ... but this is needed to satisfy the compiler :)
2440 throw new InvalidDataException (string.Format ("Unable to format attribute value ({0})", v.ToString ()));
2443 internal static IDictionary<long, string> GetEnumerationValues (TypeDefinition type)
2445 var values = new Dictionary<long, string> ();
2447 (from f in type.Fields
2448 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
2450 values [ToInt64 (f.Constant)] = f.Name;
2455 internal static long ToInt64 (object value)
2458 return (long) (ulong) value;
2459 return Convert.ToInt64 (value);
2462 private void MakeParameters (XmlElement root, MemberReference member, IList<ParameterDefinition> parameters, bool shouldDuplicateWithNew=false)
2464 XmlElement e = WriteElement(root, "Parameters");
2467 foreach (ParameterDefinition p in parameters) {
2471 var ptype = GetDocParameterType (p.ParameterType);
2472 var newPType = ptype;
2474 if (MDocUpdater.SwitchingToMagicTypes) {
2475 newPType = NativeTypeManager.ConvertFromNativeType (ptype);
2478 // now find the existing node, if it's there so we can reuse it.
2479 var nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2480 .Cast<XmlElement> ().Where (x => x.GetAttribute ("Name") == p.Name)
2483 if (nodes.Count () == 0) {
2484 // wasn't found, let's make sure it wasn't just cause the param name was changed
2485 nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2486 .Cast<XmlElement> ()
2487 .Skip (i) // this makes sure we don't inadvertently "reuse" nodes when adding new ones
2488 .Where (x => x.GetAttribute ("Name") != p.Name && (x.GetAttribute ("Type") == ptype || x.GetAttribute ("Type") == newPType))
2489 .Take(1) // there might be more than one that meets this parameter ... only take the first.
2494 x => x.GetAttribute ("Type") == ptype,
2495 x => x.SetAttribute ("Type", ptype),
2497 pe = root.OwnerDocument.CreateElement ("Parameter");
2500 pe.SetAttribute ("Name", p.Name);
2501 pe.SetAttribute ("Type", ptype);
2502 if (p.ParameterType is ByReferenceType) {
2504 pe.SetAttribute ("RefType", "out");
2506 pe.SetAttribute ("RefType", "ref");
2509 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
2518 private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams, MemberReference member, bool shouldDuplicateWithNew)
2520 if (typeParams == null || typeParams.Count == 0) {
2521 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2523 root.RemoveChild (f);
2526 XmlElement e = WriteElement(root, "TypeParameters");
2528 var nodes = e.SelectNodes ("TypeParameter").Cast<XmlElement> ().ToArray ();
2530 foreach (GenericParameter t in typeParams) {
2532 IList<TypeReference> constraints = t.Constraints;
2533 GenericParameterAttributes attrs = t.Attributes;
2539 var baseType = e.SelectSingleNode("BaseTypeName");
2540 // TODO: should this comparison take into account BaseTypeName?
2541 return x.GetAttribute("Name") == t.Name;
2543 x => {}, // no additional action required
2546 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2548 pe.SetAttribute("Name", t.Name);
2549 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""), t.DeclaringType);
2550 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2551 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2559 ce = root.OwnerDocument.CreateElement ("Constraints");
2561 pe.AppendChild (ce);
2562 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2563 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2564 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2565 AppendElementText (ce, "ParameterAttribute", "Covariant");
2566 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2567 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2568 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2569 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2570 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2571 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2572 foreach (TypeReference c in constraints) {
2573 TypeDefinition cd = c.Resolve ();
2574 AppendElementText (ce,
2575 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2576 GetDocTypeFullName (c));
2585 private void MakeParameters (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew)
2587 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2588 MakeParameters (root, mi, ((MethodDefinition)mi).Parameters, shouldDuplicateWithNew);
2589 else if (mi is MethodDefinition) {
2590 MethodDefinition mb = (MethodDefinition) mi;
2591 IList<ParameterDefinition> parameters = mb.Parameters;
2592 MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
2593 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2594 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2595 p.SetAttribute ("RefType", "this");
2598 else if (mi is PropertyDefinition) {
2599 IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
2600 if (parameters.Count > 0)
2601 MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
2605 else if (mi is FieldDefinition) return;
2606 else if (mi is EventDefinition) return;
2607 else throw new ArgumentException();
2610 internal static string GetDocParameterType (TypeReference type)
2612 return GetDocTypeFullName (type).Replace ("@", "&");
2615 private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes, bool shouldDuplicateWithNew=false)
2617 XmlElement e = WriteElement(root, "ReturnValue");
2618 var valueToUse = GetDocTypeFullName (type);
2620 AddXmlNode (e.SelectNodes("ReturnType").Cast<XmlElement> ().ToArray (),
2621 x => x.InnerText == valueToUse,
2622 x => x.InnerText = valueToUse,
2624 var newNode = WriteElementText(e, "ReturnType", valueToUse, forceNewElement: true);
2625 if (attributes != null)
2626 MakeAttributes(e, GetCustomAttributes (attributes, ""), type);
2633 private void MakeReturnValue (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew=false)
2635 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2637 else if (mi is MethodDefinition)
2638 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes, shouldDuplicateWithNew);
2639 else if (mi is PropertyDefinition)
2640 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null, shouldDuplicateWithNew);
2641 else if (mi is FieldDefinition)
2642 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null, shouldDuplicateWithNew);
2643 else if (mi is EventDefinition)
2644 MakeReturnValue (root, ((EventDefinition)mi).EventType, null, shouldDuplicateWithNew);
2646 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2649 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2651 MemberReference mi = info.Member;
2652 if (mi is TypeDefinition) return null;
2654 string sigs = memberFormatters [0].GetDeclaration (mi);
2655 if (sigs == null) return null; // not publicly visible
2657 // no documentation for property/event accessors. Is there a better way of doing this?
2658 if (mi.Name.StartsWith("get_")) return null;
2659 if (mi.Name.StartsWith("set_")) return null;
2660 if (mi.Name.StartsWith("add_")) return null;
2661 if (mi.Name.StartsWith("remove_")) return null;
2662 if (mi.Name.StartsWith("raise_")) return null;
2664 XmlElement me = doc.CreateElement("Member");
2665 me.SetAttribute("MemberName", GetMemberName (mi));
2669 if (exceptions.HasValue &&
2670 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2671 UpdateExceptions (info.Node, info.Member);
2673 if (since != null) {
2674 XmlNode docs = me.SelectSingleNode("Docs");
2675 docs.AppendChild (CreateSinceNode (doc));
2681 internal static string GetMemberName (MemberReference mi)
2683 MethodDefinition mb = mi as MethodDefinition;
2685 PropertyDefinition pi = mi as PropertyDefinition;
2688 return DocUtils.GetPropertyName (pi);
2690 StringBuilder sb = new StringBuilder (mi.Name.Length);
2691 if (!DocUtils.IsExplicitlyImplemented (mb))
2692 sb.Append (mi.Name);
2694 TypeReference iface;
2695 MethodReference ifaceMethod;
2696 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2697 sb.Append (GetDocTypeFullName (iface));
2699 sb.Append (ifaceMethod.Name);
2701 if (mb.IsGenericMethod ()) {
2702 IList<GenericParameter> typeParams = mb.GenericParameters;
2703 if (typeParams.Count > 0) {
2705 sb.Append (typeParams [0].Name);
2706 for (int i = 1; i < typeParams.Count; ++i)
2707 sb.Append (",").Append (typeParams [i].Name);
2711 return sb.ToString ();
2714 /// SIGNATURE GENERATION FUNCTIONS
2715 internal static bool IsPrivate (MemberReference mi)
2717 return memberFormatters [0].GetDeclaration (mi) == null;
2720 internal static string GetMemberType (MemberReference mi)
2722 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2723 return "Constructor";
2724 if (mi is MethodDefinition)
2726 if (mi is PropertyDefinition)
2728 if (mi is FieldDefinition)
2730 if (mi is EventDefinition)
2732 throw new ArgumentException();
2735 private static string GetDocTypeName (TypeReference type)
2737 return docTypeFormatter.GetName (type);
2740 internal static string GetDocTypeFullName (TypeReference type)
2742 return DocTypeFullMemberFormatter.Default.GetName (type);
2745 internal static string GetXPathForMember (DocumentationMember member)
2747 StringBuilder xpath = new StringBuilder ();
2748 xpath.Append ("//Members/Member[@MemberName=\"")
2749 .Append (member.MemberName)
2751 if (member.Parameters != null && member.Parameters.Count > 0) {
2752 xpath.Append ("/Parameters[count(Parameter) = ")
2753 .Append (member.Parameters.Count);
2754 for (int i = 0; i < member.Parameters.Count; ++i) {
2755 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2756 xpath.Append (member.Parameters [i]);
2757 xpath.Append ("\"");
2759 xpath.Append ("]/..");
2761 return xpath.ToString ();
2764 public static string GetXPathForMember (XPathNavigator member)
2766 StringBuilder xpath = new StringBuilder ();
2767 xpath.Append ("//Type[@FullName=\"")
2768 .Append (member.SelectSingleNode ("../../@FullName").Value)
2770 xpath.Append ("Members/Member[@MemberName=\"")
2771 .Append (member.SelectSingleNode ("@MemberName").Value)
2773 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2774 if (parameters.Count > 0) {
2775 xpath.Append ("/Parameters[count(Parameter) = ")
2776 .Append (parameters.Count);
2778 while (parameters.MoveNext ()) {
2780 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2781 xpath.Append (parameters.Current.Value);
2782 xpath.Append ("\"");
2784 xpath.Append ("]/..");
2786 return xpath.ToString ();
2789 public static string GetXPathForMember (MemberReference member)
2791 StringBuilder xpath = new StringBuilder ();
2792 xpath.Append ("//Type[@FullName=\"")
2793 .Append (member.DeclaringType.FullName)
2795 xpath.Append ("Members/Member[@MemberName=\"")
2796 .Append (GetMemberName (member))
2799 IList<ParameterDefinition> parameters = null;
2800 if (member is MethodDefinition)
2801 parameters = ((MethodDefinition) member).Parameters;
2802 else if (member is PropertyDefinition) {
2803 parameters = ((PropertyDefinition) member).Parameters;
2805 if (parameters != null && parameters.Count > 0) {
2806 xpath.Append ("/Parameters[count(Parameter) = ")
2807 .Append (parameters.Count);
2808 for (int i = 0; i < parameters.Count; ++i) {
2809 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2810 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2811 xpath.Append ("\"");
2813 xpath.Append ("]/..");
2815 return xpath.ToString ();
2819 static class CecilExtensions {
2820 public static string GetDeclaringType(this CustomAttribute attribute)
2822 var type = attribute.Constructor.DeclaringType;
2823 var typeName = type.FullName;
2825 string translatedType = NativeTypeManager.GetTranslatedName (type);
2826 return translatedType;
2829 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
2831 foreach (var c in type.Methods.Where (m => m.IsConstructor))
2832 yield return (MemberReference) c;
2833 foreach (var e in type.Events)
2834 yield return (MemberReference) e;
2835 foreach (var f in type.Fields)
2836 yield return (MemberReference) f;
2837 foreach (var m in type.Methods.Where (m => !m.IsConstructor))
2838 yield return (MemberReference) m;
2839 foreach (var t in type.NestedTypes)
2840 yield return (MemberReference) t;
2841 foreach (var p in type.Properties)
2842 yield return (MemberReference) p;
2845 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
2847 return GetMembers (type).Where (m => m.Name == member);
2850 public static MemberReference GetMember (this TypeDefinition type, string member)
2852 return GetMembers (type, member).EnsureZeroOrOne ();
2855 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2857 if (source.Count () > 1)
2858 throw new InvalidOperationException ("too many matches");
2859 return source.FirstOrDefault ();
2862 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2865 .Where (m => m.Name == method)
2866 .EnsureZeroOrOne ();
2869 public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
2871 TypeDefinition def = type as TypeDefinition;
2873 return new MemberReference [0];
2874 CustomAttribute defMemberAttr = def.CustomAttributes
2875 .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
2876 if (defMemberAttr == null)
2877 return new MemberReference [0];
2878 string name = (string) defMemberAttr.ConstructorArguments [0].Value;
2879 return def.Properties
2880 .Where (p => p.Name == name)
2881 .Select (p => (MemberReference) p);
2884 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2886 return assembly.Modules.SelectMany (md => md.GetAllTypes ());
2889 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2891 return GetTypes (assembly)
2892 .Where (td => td.FullName == type)
2893 .EnsureZeroOrOne ();
2896 public static bool IsGenericType (this TypeReference type)
2898 return type.GenericParameters.Count > 0;
2901 public static bool IsGenericMethod (this MethodReference method)
2903 return method.GenericParameters.Count > 0;
2906 public static MemberReference Resolve (this MemberReference member)
2908 FieldReference fr = member as FieldReference;
2910 return fr.Resolve ();
2911 MethodReference mr = member as MethodReference;
2913 return mr.Resolve ();
2914 TypeReference tr = member as TypeReference;
2916 return tr.Resolve ();
2917 PropertyReference pr = member as PropertyReference;
2920 EventReference er = member as EventReference;
2923 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2926 public static TypeReference GetUnderlyingType (this TypeDefinition type)
2930 return type.Fields.First (f => f.Name == "value__").FieldType;
2933 public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
2935 return self.Types.SelectMany (t => t.GetAllTypes ());
2938 static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
2942 if (!self.HasNestedTypes)
2945 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
2955 static class DocUtils {
2957 public static bool DoesNotHaveApiStyle(this XmlElement element, ApiStyle style) {
2958 string styleString = style.ToString ().ToLowerInvariant ();
2959 string apistylevalue = element.GetAttribute ("apistyle");
2960 return apistylevalue != styleString || string.IsNullOrWhiteSpace(apistylevalue);
2962 public static bool HasApiStyle(this XmlElement element, ApiStyle style) {
2963 string styleString = style.ToString ().ToLowerInvariant ();
2964 return element.GetAttribute ("apistyle") == styleString;
2966 public static bool HasApiStyle(this XmlNode node, ApiStyle style)
2968 var attribute = node.Attributes ["apistyle"];
2969 return attribute != null && attribute.Value == style.ToString ().ToLowerInvariant ();
2971 public static void AddApiStyle(this XmlElement element, ApiStyle style) {
2972 string styleString = style.ToString ().ToLowerInvariant ();
2973 var existingValue = element.GetAttribute ("apistyle");
2974 if (string.IsNullOrWhiteSpace (existingValue) || existingValue != styleString) {
2975 element.SetAttribute ("apistyle", styleString);
2978 public static void RemoveApiStyle (this XmlElement element, ApiStyle style)
2980 string styleString = style.ToString ().ToLowerInvariant ();
2981 string existingValue = element.GetAttribute ("apistyle");
2982 if (string.IsNullOrWhiteSpace (existingValue) || existingValue == styleString) {
2983 element.RemoveAttribute ("apistyle");
2986 public static void RemoveApiStyle (this XmlNode node, ApiStyle style)
2988 var styleAttribute = node.Attributes ["apistyle"];
2989 if (styleAttribute != null && styleAttribute.Value == style.ToString ().ToLowerInvariant ()) {
2990 node.Attributes.Remove (styleAttribute);
2994 public static bool IsExplicitlyImplemented (MethodDefinition method)
2996 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2999 public static string GetTypeDotMember (string name)
3001 int startType, startMethod;
3002 startType = startMethod = -1;
3003 for (int i = 0; i < name.Length; ++i) {
3004 if (name [i] == '.') {
3005 startType = startMethod;
3009 return name.Substring (startType+1);
3012 public static string GetMember (string name)
3014 int i = name.LastIndexOf ('.');
3017 return name.Substring (i+1);
3020 public static void GetInfoForExplicitlyImplementedMethod (
3021 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
3025 if (method.Overrides.Count != 1)
3026 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
3027 iface = method.Overrides [0].DeclaringType;
3028 ifaceMethod = method.Overrides [0];
3031 public static string GetPropertyName (PropertyDefinition pi)
3033 // Issue: (g)mcs-generated assemblies that explicitly implement
3034 // properties don't specify the full namespace, just the
3035 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
3036 MethodDefinition method = pi.GetMethod;
3038 method = pi.SetMethod;
3039 if (!IsExplicitlyImplemented (method))
3042 // Need to determine appropriate namespace for this member.
3043 TypeReference iface;
3044 MethodReference ifaceMethod;
3045 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3046 return string.Join (".", new string[]{
3047 DocTypeFullMemberFormatter.Default.GetName (iface),
3048 GetMember (pi.Name)});
3051 public static string GetNamespace (TypeReference type)
3053 if (type.GetElementType ().IsNested)
3054 type = type.GetElementType ();
3055 while (type != null && type.IsNested)
3056 type = type.DeclaringType;
3058 return string.Empty;
3060 string typeNS = type.Namespace;
3062 // first, make sure this isn't a type reference to another assembly/module
3064 bool isInAssembly = MDocUpdater.IsInAssemblies(type.Module.Name);
3065 if (isInAssembly && !typeNS.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (type)) {
3066 typeNS = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typeNS);
3071 public static string PathCombine (string dir, string path)
3077 return Path.Combine (dir, path);
3080 public static bool IsExtensionMethod (MethodDefinition method)
3083 method.CustomAttributes
3084 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
3085 && method.DeclaringType.CustomAttributes
3086 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
3089 public static bool IsDelegate (TypeDefinition type)
3091 TypeReference baseRef = type.BaseType;
3092 if (baseRef == null)
3094 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
3095 baseRef.FullName == "System.MulticastDelegate";
3098 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
3100 List<TypeReference> decls = new List<TypeReference> ();
3102 while (type.DeclaringType != null) {
3103 decls.Add (type.DeclaringType);
3104 type = type.DeclaringType;
3110 public static int GetGenericArgumentCount (TypeReference type)
3112 GenericInstanceType inst = type as GenericInstanceType;
3114 ? inst.GenericArguments.Count
3115 : type.GenericParameters.Count;
3118 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
3120 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
3121 List<TypeReference> userInterfaces = new List<TypeReference> ();
3122 foreach (TypeReference iface in type.Interfaces) {
3123 TypeReference lookup = iface.Resolve () ?? iface;
3124 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
3125 userInterfaces.Add (iface);
3127 return userInterfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()));
3130 private static string GetQualifiedTypeName (TypeReference type)
3132 return "[" + type.Scope.Name + "]" + type.FullName;
3135 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
3137 HashSet<string> inheritedInterfaces = new HashSet<string> ();
3138 Action<TypeDefinition> a = null;
3140 if (t == null) return;
3141 foreach (TypeReference r in t.Interfaces) {
3142 inheritedInterfaces.Add (GetQualifiedTypeName (r));
3146 TypeReference baseRef = type.BaseType;
3147 while (baseRef != null) {
3148 TypeDefinition baseDef = baseRef.Resolve ();
3149 if (baseDef != null) {
3151 baseRef = baseDef.BaseType;
3156 foreach (TypeReference r in type.Interfaces)
3158 return inheritedInterfaces;
3162 class DocsNodeInfo {
3163 public DocsNodeInfo (XmlElement node)
3168 public DocsNodeInfo (XmlElement node, TypeDefinition type)
3174 public DocsNodeInfo (XmlElement node, MemberReference member)
3177 SetMemberInfo (member);
3180 void SetType (TypeDefinition type)
3183 throw new ArgumentNullException ("type");
3185 GenericParameters = new List<GenericParameter> (type.GenericParameters);
3186 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
3187 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
3188 for (int i = 0; i < declTypes.Count - 1; ++i) {
3189 int remove = System.Math.Min (maxGenArgs,
3190 DocUtils.GetGenericArgumentCount (declTypes [i]));
3191 maxGenArgs -= remove;
3192 while (remove-- > 0)
3193 GenericParameters.RemoveAt (0);
3195 if (DocUtils.IsDelegate (type)) {
3196 Parameters = type.GetMethod("Invoke").Parameters;
3197 ReturnType = type.GetMethod("Invoke").ReturnType;
3198 ReturnIsReturn = true;
3202 void SetMemberInfo (MemberReference member)
3205 throw new ArgumentNullException ("member");
3206 ReturnIsReturn = true;
3210 if (member is MethodReference ) {
3211 MethodReference mr = (MethodReference) member;
3212 Parameters = mr.Parameters;
3213 if (mr.IsGenericMethod ()) {
3214 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
3217 else if (member is PropertyDefinition) {
3218 Parameters = ((PropertyDefinition) member).Parameters;
3221 if (member is MethodDefinition) {
3222 ReturnType = ((MethodDefinition) member).ReturnType;
3223 } else if (member is PropertyDefinition) {
3224 ReturnType = ((PropertyDefinition) member).PropertyType;
3225 ReturnIsReturn = false;
3228 // no remarks section for enum members
3229 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
3233 public TypeReference ReturnType;
3234 public List<GenericParameter> GenericParameters;
3235 public IList<ParameterDefinition> Parameters;
3236 public bool ReturnIsReturn;
3237 public XmlElement Node;
3238 public bool AddRemarks = true;
3239 public MemberReference Member;
3240 public TypeDefinition Type;
3242 public override string ToString ()
3244 return string.Format ("{0} - {1} - {2}", Type, Member, Node == null ? "no xml" : "with xml");
3248 class DocumentationEnumerator {
3250 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3252 return GetDocumentationTypes (assembly, forTypes, null);
3255 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3257 foreach (TypeDefinition type in assembly.GetTypes()) {
3258 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
3260 if (seen != null && seen.Contains (type.FullName))
3263 foreach (TypeDefinition nested in type.NestedTypes)
3264 yield return nested;
3268 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3270 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
3271 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
3272 oldmember.RemoveAttribute ("__monodocer-seen__");
3275 MemberReference m = GetMember (type, new DocumentationMember (oldmember));
3277 yield return new DocsNodeInfo (oldmember);
3280 yield return new DocsNodeInfo (oldmember, m);
3285 protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
3287 string membertype = member.MemberType;
3289 string returntype = member.ReturnType;
3291 string docName = member.MemberName;
3293 string[] docTypeParams = GetTypeParameters (docName);
3295 // If we're using 'magic types', then we might get false positives ... in those cases, we keep searching
3296 MemberReference likelyCandidate = null;
3298 // Loop through all members in this type with the same name
3299 var reflectedMembers = GetReflectionMembers (type, docName).ToArray ();
3300 foreach (MemberReference mi in reflectedMembers) {
3301 bool matchedMagicType = false;
3302 if (mi is TypeDefinition) continue;
3303 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
3305 if (MDocUpdater.IsPrivate (mi))
3308 IList<ParameterDefinition> pis = null;
3309 string[] typeParams = null;
3310 if (mi is MethodDefinition) {
3311 MethodDefinition mb = (MethodDefinition) mi;
3312 pis = mb.Parameters;
3313 if (mb.IsGenericMethod ()) {
3314 IList<GenericParameter> args = mb.GenericParameters;
3315 typeParams = args.Select (p => p.Name).ToArray ();
3318 else if (mi is PropertyDefinition)
3319 pis = ((PropertyDefinition)mi).Parameters;
3321 // check type parameters
3322 int methodTcount = member.TypeParameters == null ? 0 : member.TypeParameters.Count;
3323 int reflectionTcount = typeParams == null ? 0 : typeParams.Length;
3324 if (methodTcount != reflectionTcount)
3327 // check member parameters
3328 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
3329 int pcount = pis == null ? 0 : pis.Count;
3330 if (mcount != pcount)
3333 MethodDefinition mDef = mi as MethodDefinition;
3334 if (mDef != null && !mDef.IsConstructor) {
3335 // Casting operators can overload based on return type.
3336 string rtype = GetReplacedString (
3337 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
3338 typeParams, docTypeParams);
3339 string originalRType = rtype;
3340 if (MDocUpdater.SwitchingToMagicTypes) {
3341 rtype = NativeTypeManager.ConvertFromNativeType (rtype);
3344 if ((returntype != rtype && originalRType == rtype) ||
3345 (MDocUpdater.SwitchingToMagicTypes && returntype != originalRType && returntype != rtype && originalRType != rtype)) {
3349 if (originalRType != rtype)
3350 matchedMagicType = true;
3356 for (int i = 0; i < pis.Count; i++) {
3357 string paramType = GetReplacedString (
3358 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
3359 typeParams, docTypeParams);
3361 // if magictypes, replace paramType to "classic value" ... so the comparison works
3362 string originalParamType = paramType;
3363 if (MDocUpdater.SwitchingToMagicTypes) {
3364 paramType = NativeTypeManager.ConvertFromNativeType (paramType);
3367 string xmlMemberType = member.Parameters [i];
3368 if ((!paramType.Equals(xmlMemberType) && paramType.Equals(originalParamType)) ||
3369 (MDocUpdater.SwitchingToMagicTypes && !originalParamType.Equals(xmlMemberType) && !paramType.Equals(xmlMemberType) && !paramType.Equals(originalParamType))) {
3371 // did not match ... if we're dropping the namespace, and the paramType has the dropped
3372 // namespace, we should see if it matches when added
3373 bool stillDoesntMatch = true;
3374 if (MDocUpdater.HasDroppedNamespace(type) && paramType.StartsWith (MDocUpdater.droppedNamespace)) {
3375 string withDroppedNs = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, xmlMemberType);
3377 stillDoesntMatch = withDroppedNs != paramType;
3380 if (stillDoesntMatch) {
3386 if (originalParamType != paramType)
3387 matchedMagicType = true;
3389 if (!good) continue;
3391 if (MDocUpdater.SwitchingToMagicTypes && likelyCandidate == null && matchedMagicType) {
3392 // 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
3393 likelyCandidate = mi;
3400 return likelyCandidate;
3403 static string[] GetTypeParameters (string docName)
3405 if (docName [docName.Length-1] != '>')
3407 StringList types = new StringList ();
3408 int endToken = docName.Length-2;
3409 int i = docName.Length-2;
3411 if (docName [i] == ',' || docName [i] == '<') {
3412 types.Add (docName.Substring (i + 1, endToken - i));
3415 if (docName [i] == '<')
3420 return types.ToArray ();
3423 protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
3425 // In case of dropping the namespace, we have to remove the dropped NS
3426 // so that docName will match what's in the assembly/type
3427 if (MDocUpdater.HasDroppedNamespace (type) && docName.StartsWith(MDocUpdater.droppedNamespace + ".")) {
3428 int droppedNsLength = MDocUpdater.droppedNamespace.Length;
3429 docName = docName.Substring (droppedNsLength + 1, docName.Length - droppedNsLength - 1);
3432 // need to worry about 4 forms of //@MemberName values:
3433 // 1. "Normal" (non-generic) member names: GetEnumerator
3435 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
3436 // - try as-is, and try type.member (due to "kludge" for property
3438 // 3. "Normal" Generic member names: Sort<T> (CSC)
3439 // - need to remove generic parameters --> "Sort"
3440 // 4. Explicitly-implemented interface members for generic interfaces:
3441 // -- System.Collections.Generic.IEnumerable<T>.Current
3442 // - Try as-is, and try type.member, *keeping* the generic parameters.
3443 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
3444 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
3445 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
3446 // this as (1) or (2).
3447 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
3449 foreach (MemberReference mi in type.GetMembers (docName))
3451 if (CountChars (docName, '.') > 0)
3452 // might be a property; try only type.member instead of
3453 // namespace.type.member.
3454 foreach (MemberReference mi in
3455 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
3462 int startLt, startType, startMethod;
3463 startLt = startType = startMethod = -1;
3464 for (int i = 0; i < docName.Length; ++i) {
3465 switch (docName [i]) {
3474 if (numLt == 0 && (i + 1) < docName.Length)
3475 // there's another character in docName, so this <...> sequence is
3476 // probably part of a generic type -- case 4.
3480 startType = startMethod;
3486 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
3488 foreach (MemberReference mi in type.GetMembers (refName))
3492 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
3495 // If we _still_ haven't found it, we've hit another generic naming issue:
3496 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
3497 // explicitly-implemented METHOD names (not properties), e.g.
3498 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
3499 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
3500 // which the XML docs will contain.
3502 // Alas, we can't derive the Mono name from docName, so we need to iterate
3503 // over all member names, convert them into CSC format, and compare... :-(
3506 foreach (MemberReference mi in type.GetMembers ()) {
3507 if (MDocUpdater.GetMemberName (mi) == docName)
3512 static string GetReplacedString (string typeName, string[] from, string[] to)
3516 for (int i = 0; i < from.Length; ++i)
3517 typeName = typeName.Replace (from [i], to [i]);
3521 private static int CountChars (string s, char c)
3524 for (int i = 0; i < s.Length; ++i) {
3532 class EcmaDocumentationEnumerator : DocumentationEnumerator {
3537 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
3540 this.ecmadocs = ecmaDocs;
3543 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3545 HashSet<string> seen = new HashSet<string> ();
3546 return GetDocumentationTypes (assembly, forTypes, seen)
3547 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
3550 new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3553 while (ecmadocs.Read ()) {
3554 switch (ecmadocs.Name) {
3556 if (typeDepth == -1)
3557 typeDepth = ecmadocs.Depth;
3558 if (ecmadocs.NodeType != XmlNodeType.Element)
3560 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
3562 string typename = ecmadocs.GetAttribute ("FullName");
3563 string typename2 = MDocUpdater.GetTypeFileName (typename);
3564 if (forTypes != null &&
3565 forTypes.BinarySearch (typename) < 0 &&
3566 typename != typename2 &&
3567 forTypes.BinarySearch (typename2) < 0)
3570 if ((t = assembly.GetType (typename)) == null &&
3571 (t = assembly.GetType (typename2)) == null)
3573 seen.Add (typename);
3574 if (typename != typename2)
3575 seen.Add (typename2);
3576 Console.WriteLine (" Import: {0}", t.FullName);
3577 if (ecmadocs.Name != "Docs") {
3578 int depth = ecmadocs.Depth;
3579 while (ecmadocs.Read ()) {
3580 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
3584 if (!ecmadocs.IsStartElement ("Docs"))
3585 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
3595 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3597 return GetMembers (basefile, type)
3598 .Concat (base.GetDocumentationMembers (basefile, type));
3601 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
3603 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
3606 if (ecmadocs.IsEmptyElement)
3609 int membersDepth = ecmadocs.Depth;
3611 while (go && ecmadocs.Read ()) {
3612 switch (ecmadocs.Name) {
3614 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
3616 DocumentationMember dm = new DocumentationMember (ecmadocs);
3618 string xp = MDocUpdater.GetXPathForMember (dm);
3619 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
3621 if (oldmember == null) {
3622 m = GetMember (type, dm);
3624 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3625 type.FullName, dm.MemberSignatures ["C#"]);
3626 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
3629 // oldmember lookup may have failed due to type parameter renames.
3631 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
3632 if (oldmember == null) {
3633 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
3634 oldmember = basefile.CreateElement ("Member");
3635 oldmember.SetAttribute ("MemberName", dm.MemberName);
3636 members.AppendChild (oldmember);
3637 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
3638 XmlElement ms = basefile.CreateElement ("MemberSignature");
3639 ms.SetAttribute ("Language", key);
3640 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
3641 oldmember.AppendChild (ms);
3643 oldmember.SetAttribute ("__monodocer-seen__", "true");
3644 Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
3649 m = GetMember (type, new DocumentationMember (oldmember));
3651 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3652 type.FullName, dm.MemberSignatures ["C#"]);
3655 oldmember.SetAttribute ("__monodocer-seen__", "true");
3657 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
3658 if (ecmadocs.Name != "Docs")
3659 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
3664 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
3673 abstract class DocumentationImporter {
3675 public abstract void ImportDocumentation (DocsNodeInfo info);
3678 class MsxdocDocumentationImporter : DocumentationImporter {
3680 XmlDocument slashdocs;
3682 public MsxdocDocumentationImporter (string file)
3684 var xml = File.ReadAllText (file);
3686 // Ensure Unix line endings
3687 xml = xml.Replace ("\r", "");
3689 slashdocs = new XmlDocument();
3690 slashdocs.LoadXml (xml);
3693 public override void ImportDocumentation (DocsNodeInfo info)
3695 XmlNode elem = GetDocs (info.Member ?? info.Type);
3700 XmlElement e = info.Node;
3702 if (elem.SelectSingleNode("summary") != null)
3703 MDocUpdater.ClearElement(e, "summary");
3704 if (elem.SelectSingleNode("remarks") != null)
3705 MDocUpdater.ClearElement(e, "remarks");
3706 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3707 MDocUpdater.ClearElement(e, "value");
3708 MDocUpdater.ClearElement(e, "returns");
3711 foreach (XmlNode child in elem.ChildNodes) {
3712 switch (child.Name) {
3715 XmlAttribute name = child.Attributes ["name"];
3718 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3720 p2.InnerXml = child.InnerXml;
3723 // Occasionally XML documentation will use <returns/> on
3724 // properties, so let's try to normalize things.
3727 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3728 v.InnerXml = child.InnerXml;
3734 case "permission": {
3735 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3738 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3740 a = e.OwnerDocument.CreateElement (child.Name);
3741 a.SetAttribute ("cref", cref.Value);
3744 a.InnerXml = child.InnerXml;
3748 XmlAttribute cref = child.Attributes ["cref"];
3751 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3753 a = e.OwnerDocument.CreateElement ("altmember");
3754 a.SetAttribute ("cref", cref.Value);
3761 if (child.NodeType == XmlNodeType.Element &&
3762 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3765 MDocUpdater.CopyNode (child, e);
3772 private XmlNode GetDocs (MemberReference member)
3774 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3775 if (slashdocsig != null)
3776 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3781 class EcmaDocumentationImporter : DocumentationImporter {
3785 public EcmaDocumentationImporter (XmlReader ecmaDocs)
3787 this.ecmadocs = ecmaDocs;
3790 public override void ImportDocumentation (DocsNodeInfo info)
3792 if (!ecmadocs.IsStartElement ("Docs")) {
3796 XmlElement e = info.Node;
3798 int depth = ecmadocs.Depth;
3799 ecmadocs.ReadStartElement ("Docs");
3800 while (ecmadocs.Read ()) {
3801 if (ecmadocs.Name == "Docs") {
3802 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3805 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3807 if (!ecmadocs.IsStartElement ())
3809 switch (ecmadocs.Name) {
3812 string name = ecmadocs.GetAttribute ("name");
3815 XmlNode doc = e.SelectSingleNode (
3816 ecmadocs.Name + "[@name='" + name + "']");
3817 string value = ecmadocs.ReadInnerXml ();
3819 doc.InnerXml = value.Replace ("\r", "");
3826 string name = ecmadocs.Name;
3827 string cref = ecmadocs.GetAttribute ("cref");
3830 XmlNode doc = e.SelectSingleNode (
3831 ecmadocs.Name + "[@cref='" + cref + "']");
3832 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3834 doc.InnerXml = value;
3836 XmlElement n = e.OwnerDocument.CreateElement (name);
3837 n.SetAttribute ("cref", cref);
3844 string name = ecmadocs.Name;
3845 string xpath = ecmadocs.Name;
3846 StringList attributes = new StringList (ecmadocs.AttributeCount);
3847 if (ecmadocs.MoveToFirstAttribute ()) {
3849 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3850 } while (ecmadocs.MoveToNextAttribute ());
3851 ecmadocs.MoveToContent ();
3853 if (attributes.Count > 0) {
3854 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3856 XmlNode doc = e.SelectSingleNode (xpath);
3857 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3859 doc.InnerXml = value;
3862 XmlElement n = e.OwnerDocument.CreateElement (name);
3864 foreach (string a in attributes) {
3865 int eq = a.IndexOf ('=');
3866 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3877 class DocumentationMember {
3878 public StringToStringMap MemberSignatures = new StringToStringMap ();
3879 public string ReturnType;
3880 public StringList Parameters;
3881 public StringList TypeParameters;
3882 public string MemberName;
3883 public string MemberType;
3885 public DocumentationMember (XmlReader reader)
3887 MemberName = reader.GetAttribute ("MemberName");
3888 int depth = reader.Depth;
3890 StringList p = new StringList ();
3891 StringList tp = new StringList ();
3893 if (reader.NodeType != XmlNodeType.Element)
3896 bool shouldUse = true;
3898 string apistyle = reader.GetAttribute ("apistyle");
3899 shouldUse = string.IsNullOrWhiteSpace(apistyle) || apistyle == "classic"; // only use this tag if it's an 'classic' style node
3901 catch (Exception ex) {}
3902 switch (reader.Name) {
3903 case "MemberSignature":
3905 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3909 MemberType = reader.ReadElementString ();
3912 if (reader.Depth == depth + 2 && shouldUse)
3913 ReturnType = reader.ReadElementString ();
3916 if (reader.Depth == depth + 2 && shouldUse)
3917 p.Add (reader.GetAttribute ("Type"));
3919 case "TypeParameter":
3920 if (reader.Depth == depth + 2 && shouldUse)
3921 tp.Add (reader.GetAttribute ("Name"));
3924 if (reader.Depth == depth + 1)
3928 } while (go && reader.Read () && reader.Depth >= depth);
3933 TypeParameters = tp;
3935 DiscernTypeParameters ();
3939 public DocumentationMember (XmlNode node)
3941 MemberName = node.Attributes ["MemberName"].Value;
3942 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3943 XmlAttribute l = n.Attributes ["Language"];
3944 XmlAttribute v = n.Attributes ["Value"];
3945 XmlAttribute apistyle = n.Attributes ["apistyle"];
3946 bool shouldUse = apistyle == null || apistyle.Value == "classic";
3947 if (l != null && v != null && shouldUse)
3948 MemberSignatures [l.Value] = v.Value;
3950 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3951 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType[not(@apistyle) or @apistyle='classic']");
3953 ReturnType = rt.InnerText;
3954 XmlNodeList p = node.SelectNodes ("Parameters/Parameter[not(@apistyle) or @apistyle='classic']");
3956 Parameters = new StringList (p.Count);
3957 for (int i = 0; i < p.Count; ++i)
3958 Parameters.Add (p [i].Attributes ["Type"].Value);
3960 XmlNodeList tp = node.SelectNodes ("TypeParameters/TypeParameter[not(@apistyle) or @apistyle='classic']");
3962 TypeParameters = new StringList (tp.Count);
3963 for (int i = 0; i < tp.Count; ++i)
3964 TypeParameters.Add (tp [i].Attributes ["Name"].Value);
3967 DiscernTypeParameters ();
3971 void DiscernTypeParameters ()
3973 // see if we can discern the param list from the name
3974 if (MemberName.Contains ("<") && MemberName.EndsWith (">")) {
3975 var starti = MemberName.IndexOf ("<") + 1;
3976 var endi = MemberName.LastIndexOf (">");
3977 var paramlist = MemberName.Substring (starti, endi - starti);
3978 var tparams = paramlist.Split (new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
3979 TypeParameters = new StringList (tparams);
3984 public class DynamicParserContext {
3985 public ReadOnlyCollection<bool> TransformFlags;
3986 public int TransformIndex;
3988 public DynamicParserContext (ICustomAttributeProvider provider)
3991 if (provider.HasCustomAttributes &&
3992 (da = (provider.CustomAttributes.Cast<CustomAttribute>()
3993 .SingleOrDefault (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))) != null) {
3994 CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
3995 ? new CustomAttributeArgument [0]
3996 : (CustomAttributeArgument[]) da.ConstructorArguments [0].Value;
3998 TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool) t.Value).ToArray());
4003 public enum MemberFormatterState {
4005 WithinGenericTypeParameters,
4008 public abstract class MemberFormatter {
4010 public virtual string Language {
4014 public string GetName (MemberReference member)
4016 return GetName (member, null);
4019 public virtual string GetName (MemberReference member, DynamicParserContext context)
4021 TypeReference type = member as TypeReference;
4023 return GetTypeName (type, context);
4024 MethodReference method = member as MethodReference;
4025 if (method != null && method.Name == ".ctor") // method.IsConstructor
4026 return GetConstructorName (method);
4028 return GetMethodName (method);
4029 PropertyReference prop = member as PropertyReference;
4031 return GetPropertyName (prop);
4032 FieldReference field = member as FieldReference;
4034 return GetFieldName (field);
4035 EventReference e = member as EventReference;
4037 return GetEventName (e);
4038 throw new NotSupportedException ("Can't handle: " +
4039 (member == null ? "null" : member.GetType().ToString()));
4042 protected virtual string GetTypeName (TypeReference type)
4044 return GetTypeName (type, null);
4047 protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
4050 throw new ArgumentNullException ("type");
4051 return _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
4054 protected virtual char[] ArrayDelimeters {
4055 get {return new char[]{'[', ']'};}
4058 protected virtual MemberFormatterState MemberFormatterState { get; set; }
4060 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4062 if (type is ArrayType) {
4063 TypeSpecification spec = type as TypeSpecification;
4064 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
4065 return AppendArrayModifiers (buf, (ArrayType) type);
4067 if (type is ByReferenceType) {
4068 return AppendRefTypeName (buf, type, context);
4070 if (type is PointerType) {
4071 return AppendPointerTypeName (buf, type, context);
4073 if (type is GenericParameter) {
4074 return AppendTypeName (buf, type, context);
4076 AppendNamespace (buf, type);
4077 GenericInstanceType genInst = type as GenericInstanceType;
4078 if (type.GenericParameters.Count == 0 &&
4079 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
4080 return AppendFullTypeName (buf, type, context);
4082 return AppendGenericType (buf, type, context);
4085 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4087 string ns = DocUtils.GetNamespace (type);
4088 if (ns != null && ns.Length > 0)
4089 buf.Append (ns).Append ('.');
4093 protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4095 if (type.DeclaringType != null)
4096 AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
4097 return AppendTypeName (buf, type, context);
4100 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4102 if (context != null)
4103 context.TransformIndex++;
4104 return AppendTypeName (buf, type.Name);
4107 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
4109 int n = typename.IndexOf ("`");
4111 return buf.Append (typename.Substring (0, n));
4112 return buf.Append (typename);
4115 protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
4117 buf.Append (ArrayDelimeters [0]);
4118 int rank = array.Rank;
4120 buf.Append (new string (',', rank-1));
4121 return buf.Append (ArrayDelimeters [1]);
4124 protected virtual string RefTypeModifier {
4128 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4130 TypeSpecification spec = type as TypeSpecification;
4131 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4132 .Append (RefTypeModifier);
4135 protected virtual string PointerModifier {
4139 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4141 TypeSpecification spec = type as TypeSpecification;
4142 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4143 .Append (PointerModifier);
4146 protected virtual char[] GenericTypeContainer {
4147 get {return new char[]{'<', '>'};}
4150 protected virtual char NestedTypeSeparator {
4154 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4156 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4157 type is GenericInstanceType ? type.GetElementType () : type);
4158 List<TypeReference> genArgs = GetGenericArguments (type);
4161 bool insertNested = false;
4162 foreach (var decl in decls) {
4163 TypeReference declDef = decl.Resolve () ?? decl;
4165 buf.Append (NestedTypeSeparator);
4167 insertNested = true;
4168 AppendTypeName (buf, declDef, context);
4169 int ac = DocUtils.GetGenericArgumentCount (declDef);
4173 buf.Append (GenericTypeContainer [0]);
4174 var origState = MemberFormatterState;
4175 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4176 _AppendTypeName (buf, genArgs [argIdx++], context);
4177 for (int i = 1; i < c; ++i) {
4178 _AppendTypeName (buf.Append (","), genArgs [argIdx++], context);
4180 MemberFormatterState = origState;
4181 buf.Append (GenericTypeContainer [1]);
4187 protected List<TypeReference> GetGenericArguments (TypeReference type)
4189 var args = new List<TypeReference> ();
4190 GenericInstanceType inst = type as GenericInstanceType;
4192 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
4194 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
4198 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4203 protected virtual string GetConstructorName (MethodReference constructor)
4205 return constructor.Name;
4208 protected virtual string GetMethodName (MethodReference method)
4213 protected virtual string GetPropertyName (PropertyReference property)
4215 return property.Name;
4218 protected virtual string GetFieldName (FieldReference field)
4223 protected virtual string GetEventName (EventReference e)
4228 public virtual string GetDeclaration (MemberReference member)
4231 throw new ArgumentNullException ("member");
4232 TypeDefinition type = member as TypeDefinition;
4234 return GetTypeDeclaration (type);
4235 MethodDefinition method = member as MethodDefinition;
4236 if (method != null && method.IsConstructor)
4237 return GetConstructorDeclaration (method);
4239 return GetMethodDeclaration (method);
4240 PropertyDefinition prop = member as PropertyDefinition;
4242 return GetPropertyDeclaration (prop);
4243 FieldDefinition field = member as FieldDefinition;
4245 return GetFieldDeclaration (field);
4246 EventDefinition e = member as EventDefinition;
4248 return GetEventDeclaration (e);
4249 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
4252 protected virtual string GetTypeDeclaration (TypeDefinition type)
4255 throw new ArgumentNullException ("type");
4256 StringBuilder buf = new StringBuilder (type.Name.Length);
4257 _AppendTypeName (buf, type, null);
4258 AppendGenericTypeConstraints (buf, type);
4259 return buf.ToString ();
4262 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
4264 return GetConstructorName (constructor);
4267 protected virtual string GetMethodDeclaration (MethodDefinition method)
4269 if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
4270 ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
4273 // Special signature for destructors.
4274 if (method.Name == "Finalize" && method.Parameters.Count == 0)
4275 return GetFinalizerName (method);
4277 StringBuilder buf = new StringBuilder ();
4279 AppendVisibility (buf, method);
4280 if (buf.Length == 0 &&
4281 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
4284 AppendModifiers (buf, method);
4286 if (buf.Length != 0)
4288 buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
4290 AppendMethodName (buf, method);
4291 AppendGenericMethod (buf, method).Append (" ");
4292 AppendParameters (buf, method, method.Parameters);
4293 AppendGenericMethodConstraints (buf, method);
4294 return buf.ToString ();
4297 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4299 return buf.Append (method.Name);
4302 protected virtual string GetFinalizerName (MethodDefinition method)
4307 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4312 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4317 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4322 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4327 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4332 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
4334 return GetPropertyName (property);
4337 protected virtual string GetFieldDeclaration (FieldDefinition field)
4339 return GetFieldName (field);
4342 protected virtual string GetEventDeclaration (EventDefinition e)
4344 return GetEventName (e);
4348 class ILFullMemberFormatter : MemberFormatter {
4350 public override string Language {
4351 get {return "ILAsm";}
4354 protected override char NestedTypeSeparator {
4360 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4362 if (GetBuiltinType (type.FullName) != null)
4364 string ns = DocUtils.GetNamespace (type);
4365 if (ns != null && ns.Length > 0) {
4366 if (type.IsValueType)
4367 buf.Append ("valuetype ");
4369 buf.Append ("class ");
4370 buf.Append (ns).Append ('.');
4375 protected static string GetBuiltinType (string t)
4378 case "System.Byte": return "unsigned int8";
4379 case "System.SByte": return "int8";
4380 case "System.Int16": return "int16";
4381 case "System.Int32": return "int32";
4382 case "System.Int64": return "int64";
4383 case "System.IntPtr": return "native int";
4385 case "System.UInt16": return "unsigned int16";
4386 case "System.UInt32": return "unsigned int32";
4387 case "System.UInt64": return "unsigned int64";
4388 case "System.UIntPtr": return "native unsigned int";
4390 case "System.Single": return "float32";
4391 case "System.Double": return "float64";
4392 case "System.Boolean": return "bool";
4393 case "System.Char": return "char";
4394 case "System.Void": return "void";
4395 case "System.String": return "string";
4396 case "System.Object": return "object";
4401 protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
4403 return buf.Append (typename);
4406 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4408 if (type is GenericParameter) {
4409 AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
4413 string s = GetBuiltinType (type.FullName);
4415 return buf.Append (s);
4417 return base.AppendTypeName (buf, type, context);
4420 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
4422 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
4423 return buf.Append (type.Owner is TypeReference ? "!" : "!!");
4425 GenericParameterAttributes attrs = type.Attributes;
4426 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
4427 buf.Append ("class ");
4428 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
4429 buf.Append ("struct ");
4430 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
4431 buf.Append (".ctor ");
4432 IList<TypeReference> constraints = type.Constraints;
4433 MemberFormatterState = 0;
4434 if (constraints.Count > 0) {
4435 var full = new ILFullMemberFormatter ();
4436 buf.Append ("(").Append (full.GetName (constraints [0]));
4437 for (int i = 1; i < constraints.Count; ++i) {
4438 buf.Append (", ").Append (full.GetName (constraints [i]));
4442 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4444 if ((attrs & GenericParameterAttributes.Covariant) != 0)
4446 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
4451 protected override string GetTypeDeclaration (TypeDefinition type)
4453 string visibility = GetTypeVisibility (type.Attributes);
4454 if (visibility == null)
4457 StringBuilder buf = new StringBuilder ();
4459 buf.Append (".class ");
4461 buf.Append ("nested ");
4462 buf.Append (visibility).Append (" ");
4463 if (type.IsInterface)
4464 buf.Append ("interface ");
4465 if (type.IsSequentialLayout)
4466 buf.Append ("sequential ");
4467 if (type.IsAutoLayout)
4468 buf.Append ("auto ");
4469 if (type.IsAnsiClass)
4470 buf.Append ("ansi ");
4471 if (type.IsAbstract)
4472 buf.Append ("abstract ");
4473 if (type.IsSerializable)
4474 buf.Append ("serializable ");
4476 buf.Append ("sealed ");
4477 if (type.IsBeforeFieldInit)
4478 buf.Append ("beforefieldinit ");
4479 var state = MemberFormatterState;
4480 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4481 buf.Append (GetName (type));
4482 MemberFormatterState = state;
4483 var full = new ILFullMemberFormatter ();
4484 if (type.BaseType != null) {
4485 buf.Append (" extends ");
4486 if (type.BaseType.FullName == "System.Object")
4487 buf.Append ("System.Object");
4489 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
4492 foreach (var name in type.Interfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()))
4493 .Select (i => full.GetName (i))
4494 .OrderBy (n => n)) {
4496 buf.Append (" implements ");
4505 return buf.ToString ();
4508 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4510 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4511 type is GenericInstanceType ? type.GetElementType () : type);
4513 foreach (var decl in decls) {
4514 TypeReference declDef = decl.Resolve () ?? decl;
4516 buf.Append (NestedTypeSeparator);
4519 AppendTypeName (buf, declDef, context);
4523 foreach (TypeReference arg in GetGenericArguments (type)) {
4527 _AppendTypeName (buf, arg, context);
4533 static string GetTypeVisibility (TypeAttributes ta)
4535 switch (ta & TypeAttributes.VisibilityMask) {
4536 case TypeAttributes.Public:
4537 case TypeAttributes.NestedPublic:
4540 case TypeAttributes.NestedFamily:
4541 case TypeAttributes.NestedFamORAssem:
4549 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4551 return GetMethodDeclaration (constructor);
4554 protected override string GetMethodDeclaration (MethodDefinition method)
4556 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
4559 var buf = new StringBuilder ();
4560 buf.Append (".method ");
4561 AppendVisibility (buf, method);
4562 if (method.IsStatic)
4563 buf.Append ("static ");
4564 if (method.IsHideBySig)
4565 buf.Append ("hidebysig ");
4566 if (method.IsPInvokeImpl) {
4567 var info = method.PInvokeInfo;
4568 buf.Append ("pinvokeimpl (\"")
4569 .Append (info.Module.Name)
4570 .Append ("\" as \"")
4571 .Append (info.EntryPoint)
4573 if (info.IsCharSetAuto)
4574 buf.Append (" auto");
4575 if (info.IsCharSetUnicode)
4576 buf.Append (" unicode");
4577 if (info.IsCharSetAnsi)
4578 buf.Append (" ansi");
4579 if (info.IsCallConvCdecl)
4580 buf.Append (" cdecl");
4581 if (info.IsCallConvStdCall)
4582 buf.Append (" stdcall");
4583 if (info.IsCallConvWinapi)
4584 buf.Append (" winapi");
4585 if (info.IsCallConvThiscall)
4586 buf.Append (" thiscall");
4587 if (info.SupportsLastError)
4588 buf.Append (" lasterr");
4591 if (method.IsSpecialName)
4592 buf.Append ("specialname ");
4593 if (method.IsRuntimeSpecialName)
4594 buf.Append ("rtspecialname ");
4595 if (method.IsNewSlot)
4596 buf.Append ("newslot ");
4597 if (method.IsVirtual)
4598 buf.Append ("virtual ");
4599 if (!method.IsStatic)
4600 buf.Append ("instance ");
4601 _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
4603 .Append (method.Name);
4604 if (method.IsGenericMethod ()) {
4605 var state = MemberFormatterState;
4606 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4607 IList<GenericParameter> args = method.GenericParameters;
4608 if (args.Count > 0) {
4610 _AppendTypeName (buf, args [0], null);
4611 for (int i = 1; i < args.Count; ++i)
4612 _AppendTypeName (buf.Append (", "), args [i], null);
4615 MemberFormatterState = state;
4620 for (int i = 0; i < method.Parameters.Count; ++i) {
4624 _AppendTypeName (buf, method.Parameters [i].ParameterType, new DynamicParserContext (method.Parameters [i]));
4626 buf.Append (method.Parameters [i].Name);
4630 buf.Append (" cil");
4631 if (method.IsRuntime)
4632 buf.Append (" runtime");
4633 if (method.IsManaged)
4634 buf.Append (" managed");
4636 return buf.ToString ();
4639 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4641 if (DocUtils.IsExplicitlyImplemented (method)) {
4642 TypeReference iface;
4643 MethodReference ifaceMethod;
4644 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4645 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4647 .Append (ifaceMethod.Name);
4649 return base.AppendMethodName (buf, method);
4652 protected override string RefTypeModifier {
4656 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4658 if (method.IsPublic)
4659 return buf.Append ("public ");
4660 if (method.IsFamilyAndAssembly)
4661 return buf.Append ("familyandassembly");
4662 if (method.IsFamilyOrAssembly)
4663 return buf.Append ("familyorassembly");
4664 if (method.IsFamily)
4665 return buf.Append ("family");
4669 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4671 string modifiers = String.Empty;
4672 if (method.IsStatic) modifiers += " static";
4673 if (method.IsVirtual && !method.IsAbstract) {
4674 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4675 else modifiers += " override";
4677 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4678 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4679 if (method.IsFinal) modifiers += " sealed";
4680 if (modifiers == " virtual sealed") modifiers = "";
4682 return buf.Append (modifiers);
4685 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4687 if (method.IsGenericMethod ()) {
4688 IList<GenericParameter> args = method.GenericParameters;
4689 if (args.Count > 0) {
4691 buf.Append (args [0].Name);
4692 for (int i = 1; i < args.Count; ++i)
4693 buf.Append (",").Append (args [i].Name);
4700 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4702 return AppendParameters (buf, method, parameters, '(', ')');
4705 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4709 if (parameters.Count > 0) {
4710 if (DocUtils.IsExtensionMethod (method))
4711 buf.Append ("this ");
4712 AppendParameter (buf, parameters [0]);
4713 for (int i = 1; i < parameters.Count; ++i) {
4715 AppendParameter (buf, parameters [i]);
4719 return buf.Append (end);
4722 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4724 if (parameter.ParameterType is ByReferenceType) {
4725 if (parameter.IsOut)
4726 buf.Append ("out ");
4728 buf.Append ("ref ");
4730 buf.Append (GetName (parameter.ParameterType)).Append (" ");
4731 return buf.Append (parameter.Name);
4734 protected override string GetPropertyDeclaration (PropertyDefinition property)
4736 MethodDefinition gm = null, sm = null;
4738 string get_visible = null;
4739 if ((gm = property.GetMethod) != null &&
4740 (DocUtils.IsExplicitlyImplemented (gm) ||
4741 (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
4742 get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
4743 string set_visible = null;
4744 if ((sm = property.SetMethod) != null &&
4745 (DocUtils.IsExplicitlyImplemented (sm) ||
4746 (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
4747 set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
4749 if ((set_visible == null) && (get_visible == null))
4752 StringBuilder buf = new StringBuilder ()
4753 .Append (".property ");
4754 if (!(gm ?? sm).IsStatic)
4755 buf.Append ("instance ");
4756 _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
4757 buf.Append (' ').Append (property.Name);
4758 if (!property.HasParameters || property.Parameters.Count == 0)
4759 return buf.ToString ();
4763 foreach (ParameterDefinition p in property.Parameters) {
4767 _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
4771 return buf.ToString ();
4774 protected override string GetFieldDeclaration (FieldDefinition field)
4776 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4777 if (declType.IsEnum && field.Name == "value__")
4778 return null; // This member of enums aren't documented.
4780 StringBuilder buf = new StringBuilder ();
4781 AppendFieldVisibility (buf, field);
4782 if (buf.Length == 0)
4785 buf.Insert (0, ".field ");
4788 buf.Append ("static ");
4789 if (field.IsInitOnly)
4790 buf.Append ("initonly ");
4791 if (field.IsLiteral)
4792 buf.Append ("literal ");
4793 _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
4794 buf.Append (' ').Append (field.Name);
4795 AppendFieldValue (buf, field);
4797 return buf.ToString ();
4800 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4803 return buf.Append ("public ");
4804 if (field.IsFamilyAndAssembly)
4805 return buf.Append ("familyandassembly ");
4806 if (field.IsFamilyOrAssembly)
4807 return buf.Append ("familyorassembly ");
4809 return buf.Append ("family ");
4813 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4815 // enums have a value__ field, which we ignore
4816 if (field.DeclaringType.IsGenericType ())
4818 if (field.HasConstant && field.IsLiteral) {
4821 val = field.Constant;
4826 buf.Append (" = ").Append ("null");
4827 else if (val is Enum)
4829 .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4831 .Append (val.ToString ())
4833 else if (val is IFormattable) {
4834 string value = ((IFormattable)val).ToString();
4837 buf.Append ("\"" + value + "\"");
4839 buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4848 protected override string GetEventDeclaration (EventDefinition e)
4850 StringBuilder buf = new StringBuilder ();
4851 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4856 buf.Append (".event ")
4857 .Append (GetName (e.EventType))
4861 return buf.ToString ();
4865 class ILMemberFormatter : ILFullMemberFormatter {
4866 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4872 class ILNativeTypeMemberFormatter : ILFullMemberFormatter {
4873 protected static string _GetBuiltinType (string t)
4875 //string moddedType = base.GetBuiltinType (t);
4877 //return moddedType;
4881 class CSharpNativeTypeMemberFormatter : CSharpFullMemberFormatter {
4882 protected override string GetCSharpType (string t) {
4883 string moddedType = base.GetCSharpType (t);
4885 switch (moddedType) {
4886 case "int": return "nint";
4891 case "System.Drawing.SizeF":
4892 return "CoreGraphics.CGSize";
4893 case "System.Drawing.PointF":
4894 return "CoreGraphics.CGPoint";
4895 case "System.Drawing.RectangleF":
4896 return "CoreGraphics.CGPoint";
4902 class CSharpFullMemberFormatter : MemberFormatter {
4904 public override string Language {
4908 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4911 string ns = DocUtils.GetNamespace (type);
4912 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
4913 buf.Append (ns).Append ('.');
4917 protected virtual string GetCSharpType (string t)
4920 case "System.Byte": return "byte";
4921 case "System.SByte": return "sbyte";
4922 case "System.Int16": return "short";
4923 case "System.Int32": return "int";
4924 case "System.Int64": return "long";
4926 case "System.UInt16": return "ushort";
4927 case "System.UInt32": return "uint";
4928 case "System.UInt64": return "ulong";
4930 case "System.Single": return "float";
4931 case "System.Double": return "double";
4932 case "System.Decimal": return "decimal";
4933 case "System.Boolean": return "bool";
4934 case "System.Char": return "char";
4935 case "System.Void": return "void";
4936 case "System.String": return "string";
4937 case "System.Object": return "object";
4942 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4944 if (context != null && context.TransformFlags != null &&
4945 (context.TransformFlags.Count == 0 || context.TransformFlags [context.TransformIndex])) {
4946 context.TransformIndex++;
4947 return buf.Append ("dynamic");
4950 if (type is GenericParameter)
4951 return AppendGenericParameterConstraints (buf, (GenericParameter) type, context).Append (type.Name);
4952 string t = type.FullName;
4953 if (!t.StartsWith ("System.")) {
4954 return base.AppendTypeName (buf, type, context);
4957 string s = GetCSharpType (t);
4959 if (context != null)
4960 context.TransformIndex++;
4961 return buf.Append (s);
4964 return base.AppendTypeName (buf, type, context);
4967 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
4969 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
4971 GenericParameterAttributes attrs = type.Attributes;
4972 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
4973 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
4977 buf.Append ("out ");
4981 protected override string GetTypeDeclaration (TypeDefinition type)
4983 string visibility = GetTypeVisibility (type.Attributes);
4984 if (visibility == null)
4987 StringBuilder buf = new StringBuilder ();
4989 buf.Append (visibility);
4992 MemberFormatter full = new CSharpFullMemberFormatter ();
4994 if (DocUtils.IsDelegate (type)) {
4995 buf.Append("delegate ");
4996 MethodDefinition invoke = type.GetMethod ("Invoke");
4997 buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
4998 buf.Append (GetName (type));
4999 AppendParameters (buf, invoke, invoke.Parameters);
5000 AppendGenericTypeConstraints (buf, type);
5003 return buf.ToString();
5006 if (type.IsAbstract && !type.IsInterface)
5007 buf.Append("abstract ");
5008 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
5009 buf.Append("sealed ");
5010 buf.Replace ("abstract sealed", "static");
5012 buf.Append (GetTypeKind (type));
5014 buf.Append (GetCSharpType (type.FullName) == null
5019 TypeReference basetype = type.BaseType;
5020 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
5023 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
5024 .Select (iface => full.GetName (iface))
5028 if (basetype != null || interface_names.Count > 0)
5031 if (basetype != null) {
5032 buf.Append (full.GetName (basetype));
5033 if (interface_names.Count > 0)
5037 for (int i = 0; i < interface_names.Count; i++){
5040 buf.Append (interface_names [i]);
5042 AppendGenericTypeConstraints (buf, type);
5045 return buf.ToString ();
5048 static string GetTypeKind (TypeDefinition t)
5054 if (t.IsClass || t.FullName == "System.Enum")
5058 throw new ArgumentException(t.FullName);
5061 static string GetTypeVisibility (TypeAttributes ta)
5063 switch (ta & TypeAttributes.VisibilityMask) {
5064 case TypeAttributes.Public:
5065 case TypeAttributes.NestedPublic:
5068 case TypeAttributes.NestedFamily:
5069 case TypeAttributes.NestedFamORAssem:
5077 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
5079 if (type.GenericParameters.Count == 0)
5081 return AppendConstraints (buf, type.GenericParameters);
5084 private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
5086 foreach (GenericParameter genArg in genArgs) {
5087 GenericParameterAttributes attrs = genArg.Attributes;
5088 IList<TypeReference> constraints = genArg.Constraints;
5089 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
5092 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
5093 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
5094 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
5097 if (!isref && !isvt && !isnew && constraints.Count == 0)
5099 buf.Append (" where ").Append (genArg.Name).Append (" : ");
5101 buf.Append ("class");
5105 buf.Append ("struct");
5108 if (constraints.Count > 0 && !isvt) {
5111 buf.Append (GetTypeName (constraints [0]));
5112 for (int i = 1; i < constraints.Count; ++i)
5113 buf.Append (", ").Append (GetTypeName (constraints [i]));
5115 if (isnew && !isvt) {
5118 buf.Append ("new()");
5124 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5126 StringBuilder buf = new StringBuilder ();
5127 AppendVisibility (buf, constructor);
5128 if (buf.Length == 0)
5132 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
5133 AppendParameters (buf, constructor, constructor.Parameters);
5136 return buf.ToString ();
5139 protected override string GetMethodDeclaration (MethodDefinition method)
5141 string decl = base.GetMethodDeclaration (method);
5147 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
5149 if (DocUtils.IsExplicitlyImplemented (method)) {
5150 TypeReference iface;
5151 MethodReference ifaceMethod;
5152 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5153 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
5155 .Append (ifaceMethod.Name);
5157 return base.AppendMethodName (buf, method);
5160 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
5162 if (method.GenericParameters.Count == 0)
5164 return AppendConstraints (buf, method.GenericParameters);
5167 protected override string RefTypeModifier {
5171 protected override string GetFinalizerName (MethodDefinition method)
5173 return "~" + method.DeclaringType.Name + " ()";
5176 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
5180 if (method.IsPublic)
5181 return buf.Append ("public");
5182 if (method.IsFamily || method.IsFamilyOrAssembly)
5183 return buf.Append ("protected");
5187 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
5189 string modifiers = String.Empty;
5190 if (method.IsStatic) modifiers += " static";
5191 if (method.IsVirtual && !method.IsAbstract) {
5192 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
5193 else modifiers += " override";
5195 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
5196 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
5197 if (method.IsFinal) modifiers += " sealed";
5198 if (modifiers == " virtual sealed") modifiers = "";
5200 return buf.Append (modifiers);
5203 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
5205 if (method.IsGenericMethod ()) {
5206 IList<GenericParameter> args = method.GenericParameters;
5207 if (args.Count > 0) {
5209 buf.Append (args [0].Name);
5210 for (int i = 1; i < args.Count; ++i)
5211 buf.Append (",").Append (args [i].Name);
5218 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
5220 return AppendParameters (buf, method, parameters, '(', ')');
5223 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
5227 if (parameters.Count > 0) {
5228 if (DocUtils.IsExtensionMethod (method))
5229 buf.Append ("this ");
5230 AppendParameter (buf, parameters [0]);
5231 for (int i = 1; i < parameters.Count; ++i) {
5233 AppendParameter (buf, parameters [i]);
5237 return buf.Append (end);
5240 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
5242 if (parameter.ParameterType is ByReferenceType) {
5243 if (parameter.IsOut)
5244 buf.Append ("out ");
5246 buf.Append ("ref ");
5248 buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
5249 buf.Append (parameter.Name);
5250 if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant) {
5251 buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
5256 protected override string GetPropertyDeclaration (PropertyDefinition property)
5258 MethodDefinition method;
5260 string get_visible = null;
5261 if ((method = property.GetMethod) != null &&
5262 (DocUtils.IsExplicitlyImplemented (method) ||
5263 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5264 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5265 string set_visible = null;
5266 if ((method = property.SetMethod) != null &&
5267 (DocUtils.IsExplicitlyImplemented (method) ||
5268 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5269 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5271 if ((set_visible == null) && (get_visible == null))
5275 StringBuilder buf = new StringBuilder ();
5276 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
5277 buf.Append (visibility = get_visible);
5278 else if (set_visible != null && get_visible == null)
5279 buf.Append (visibility = set_visible);
5281 buf.Append (visibility = "public");
5283 // Pick an accessor to use for static/virtual/override/etc. checks.
5284 method = property.SetMethod;
5286 method = property.GetMethod;
5288 string modifiers = String.Empty;
5289 if (method.IsStatic) modifiers += " static";
5290 if (method.IsVirtual && !method.IsAbstract) {
5291 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
5292 modifiers += " virtual";
5294 modifiers += " override";
5296 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
5297 if (method.IsAbstract && !declDef.IsInterface)
5298 modifiers += " abstract";
5300 modifiers += " sealed";
5301 if (modifiers == " virtual sealed")
5303 buf.Append (modifiers).Append (' ');
5305 buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
5307 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
5308 string name = property.Name;
5309 foreach (MemberReference mi in defs) {
5310 if (mi == property) {
5315 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
5317 if (property.Parameters.Count != 0) {
5318 AppendParameters (buf, method, property.Parameters, '[', ']');
5322 if (get_visible != null) {
5323 if (get_visible != visibility)
5324 buf.Append (' ').Append (get_visible);
5325 buf.Append (" get;");
5327 if (set_visible != null) {
5328 if (set_visible != visibility)
5329 buf.Append (' ').Append (set_visible);
5330 buf.Append (" set;");
5334 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
5337 protected override string GetFieldDeclaration (FieldDefinition field)
5339 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
5340 if (declType.IsEnum && field.Name == "value__")
5341 return null; // This member of enums aren't documented.
5343 StringBuilder buf = new StringBuilder ();
5344 AppendFieldVisibility (buf, field);
5345 if (buf.Length == 0)
5348 if (declType.IsEnum)
5351 if (field.IsStatic && !field.IsLiteral)
5352 buf.Append (" static");
5353 if (field.IsInitOnly)
5354 buf.Append (" readonly");
5355 if (field.IsLiteral)
5356 buf.Append (" const");
5358 buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
5359 buf.Append (field.Name);
5360 AppendFieldValue (buf, field);
5363 return buf.ToString ();
5366 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
5369 return buf.Append ("public");
5370 if (field.IsFamily || field.IsFamilyOrAssembly)
5371 return buf.Append ("protected");
5375 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
5377 // enums have a value__ field, which we ignore
5378 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
5379 field.DeclaringType.IsGenericType ())
5381 if (field.HasConstant && field.IsLiteral) {
5384 val = field.Constant;
5389 buf.Append (" = ").Append ("null");
5390 else if (val is Enum)
5391 buf.Append (" = ").Append (val.ToString ());
5392 else if (val is IFormattable) {
5393 string value = ((IFormattable)val).ToString();
5395 value = "\"" + value + "\"";
5396 buf.Append (" = ").Append (value);
5402 protected override string GetEventDeclaration (EventDefinition e)
5404 StringBuilder buf = new StringBuilder ();
5405 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
5409 AppendModifiers (buf, e.AddMethod);
5411 buf.Append (" event ");
5412 buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters [0]))).Append (' ');
5413 buf.Append (e.Name).Append (';');
5415 return buf.ToString ();
5419 class CSharpMemberFormatter : CSharpFullMemberFormatter {
5420 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5426 class DocTypeFullMemberFormatter : MemberFormatter {
5427 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
5429 protected override char NestedTypeSeparator {
5434 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
5435 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5441 class SlashDocMemberFormatter : MemberFormatter {
5443 protected override char[] GenericTypeContainer {
5444 get {return new char[]{'{', '}'};}
5447 private bool AddTypeCount = true;
5449 private TypeReference genDeclType;
5450 private MethodReference genDeclMethod;
5452 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
5454 if (type is GenericParameter) {
5456 if (genDeclType != null) {
5457 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
5458 for (int i = 0; i < genArgs.Count; ++i) {
5459 if (genArgs [i].Name == type.Name) {
5460 buf.Append ('`').Append (i);
5465 if (genDeclMethod != null) {
5466 IList<GenericParameter> genArgs = null;
5467 if (genDeclMethod.IsGenericMethod ()) {
5468 genArgs = genDeclMethod.GenericParameters;
5469 for (int i = 0; i < genArgs.Count; ++i) {
5470 if (genArgs [i].Name == type.Name) {
5471 buf.Append ("``").Append (i);
5477 if (genDeclType == null && genDeclMethod == null) {
5478 // Probably from within an explicitly implemented interface member,
5479 // where CSC uses parameter names instead of indices (why?), e.g.
5480 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
5481 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
5482 buf.Append (type.Name);
5484 if (buf.Length == l) {
5485 throw new Exception (string.Format (
5486 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
5487 type.Name, genDeclType, genDeclMethod));
5491 base.AppendTypeName (buf, type, context);
5493 int numArgs = type.GenericParameters.Count;
5494 if (type.DeclaringType != null)
5495 numArgs -= type.GenericParameters.Count;
5497 buf.Append ('`').Append (numArgs);
5504 protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
5506 buf.Append (ArrayDelimeters [0]);
5507 int rank = array.Rank;
5510 for (int i = 1; i < rank; ++i) {
5514 return buf.Append (ArrayDelimeters [1]);
5517 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5520 base.AppendGenericType (buf, type, context);
5522 AppendType (buf, type, context);
5526 private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5528 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
5529 bool insertNested = false;
5530 int prevParamCount = 0;
5531 foreach (var decl in decls) {
5533 buf.Append (NestedTypeSeparator);
5534 insertNested = true;
5535 base.AppendTypeName (buf, decl, context);
5536 int argCount = DocUtils.GetGenericArgumentCount (decl);
5537 int numArgs = argCount - prevParamCount;
5538 prevParamCount = argCount;
5540 buf.Append ('`').Append (numArgs);
5545 public override string GetDeclaration (MemberReference member)
5547 TypeReference r = member as TypeReference;
5549 return "T:" + GetTypeName (r);
5551 return base.GetDeclaration (member);
5554 protected override string GetConstructorName (MethodReference constructor)
5556 return GetMethodDefinitionName (constructor, "#ctor");
5559 protected override string GetMethodName (MethodReference method)
5562 MethodDefinition methodDef = method as MethodDefinition;
5563 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
5566 TypeReference iface;
5567 MethodReference ifaceMethod;
5568 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
5569 AddTypeCount = false;
5570 name = GetTypeName (iface) + "." + ifaceMethod.Name;
5571 AddTypeCount = true;
5573 return GetMethodDefinitionName (method, name);
5576 private string GetMethodDefinitionName (MethodReference method, string name)
5578 StringBuilder buf = new StringBuilder ();
5579 buf.Append (GetTypeName (method.DeclaringType));
5581 buf.Append (name.Replace (".", "#"));
5582 if (method.IsGenericMethod ()) {
5583 IList<GenericParameter> genArgs = method.GenericParameters;
5584 if (genArgs.Count > 0)
5585 buf.Append ("``").Append (genArgs.Count);
5587 IList<ParameterDefinition> parameters = method.Parameters;
5589 genDeclType = method.DeclaringType;
5590 genDeclMethod = method;
5591 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
5595 genDeclMethod = null;
5597 return buf.ToString ();
5600 private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
5602 if (parameters.Count == 0)
5607 AppendParameter (buf, genArgs, parameters [0]);
5608 for (int i = 1; i < parameters.Count; ++i) {
5610 AppendParameter (buf, genArgs, parameters [i]);
5613 return buf.Append (')');
5616 private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
5618 AddTypeCount = false;
5619 buf.Append (GetTypeName (parameter.ParameterType));
5620 AddTypeCount = true;
5624 protected override string GetPropertyName (PropertyReference property)
5628 PropertyDefinition propertyDef = property as PropertyDefinition;
5629 MethodDefinition method = null;
5630 if (propertyDef != null)
5631 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
5632 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
5633 name = property.Name;
5635 TypeReference iface;
5636 MethodReference ifaceMethod;
5637 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5638 AddTypeCount = false;
5639 name = string.Join ("#", new string[]{
5640 GetTypeName (iface).Replace (".", "#"),
5641 DocUtils.GetMember (property.Name)
5643 AddTypeCount = true;
5646 StringBuilder buf = new StringBuilder ();
5647 buf.Append (GetName (property.DeclaringType));
5650 IList<ParameterDefinition> parameters = property.Parameters;
5651 if (parameters.Count > 0) {
5652 genDeclType = property.DeclaringType;
5654 IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
5655 AppendParameter (buf, genArgs, parameters [0]);
5656 for (int i = 1; i < parameters.Count; ++i) {
5658 AppendParameter (buf, genArgs, parameters [i]);
5663 return buf.ToString ();
5666 protected override string GetFieldName (FieldReference field)
5668 return string.Format ("{0}.{1}",
5669 GetName (field.DeclaringType), field.Name);
5672 protected override string GetEventName (EventReference e)
5674 return string.Format ("{0}.{1}",
5675 GetName (e.DeclaringType), e.Name);
5678 protected override string GetTypeDeclaration (TypeDefinition type)
5680 string name = GetName (type);
5686 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5688 string name = GetName (constructor);
5694 protected override string GetMethodDeclaration (MethodDefinition method)
5696 string name = GetName (method);
5699 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
5700 genDeclType = method.DeclaringType;
5701 genDeclMethod = method;
5702 name += "~" + GetName (method.ReturnType);
5704 genDeclMethod = null;
5709 protected override string GetPropertyDeclaration (PropertyDefinition property)
5711 string name = GetName (property);
5717 protected override string GetFieldDeclaration (FieldDefinition field)
5719 string name = GetName (field);
5725 protected override string GetEventDeclaration (EventDefinition e)
5727 string name = GetName (e);
5734 class FileNameMemberFormatter : SlashDocMemberFormatter {
5735 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5740 protected override char NestedTypeSeparator {
5745 class ResolvedTypeInfo {
5746 TypeDefinition typeDef;
5748 public ResolvedTypeInfo (TypeReference value) {
5752 public TypeReference Reference { get; private set; }
5754 public TypeDefinition Definition {
5756 if (typeDef == null) {
5757 typeDef = Reference.Resolve ();
5764 /// <summary>Formats attribute values. Should return true if it is able to format the value.</summary>
5765 class AttributeValueFormatter {
5766 public virtual bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5768 TypeReference valueType = type.Reference;
5770 returnvalue = "null";
5773 if (valueType.FullName == "System.Type") {
5774 var vTypeRef = v as TypeReference;
5775 if (vTypeRef != null)
5776 returnvalue = "typeof(" + NativeTypeManager.GetTranslatedName (vTypeRef) + ")"; // TODO: drop NS handling
5778 returnvalue = "typeof(" + v.ToString () + ")";
5782 if (valueType.FullName == "System.String") {
5783 returnvalue = "\"" + v.ToString () + "\"";
5786 if (valueType.FullName == "System.Char") {
5787 returnvalue = "'" + v.ToString () + "'";
5791 returnvalue = (bool)v ? "true" : "false";
5795 TypeDefinition valueDef = type.Definition;
5796 if (valueDef == null || !valueDef.IsEnum) {
5797 returnvalue = v.ToString ();
5801 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5802 var values = MDocUpdater.GetEnumerationValues (valueDef);
5803 long c = MDocUpdater.ToInt64 (v);
5804 if (values.ContainsKey (c)) {
5805 returnvalue = typename + "." + values [c];
5814 /// <summary>The final value formatter in the pipeline ... if no other formatter formats the value,
5815 /// then this one will serve as the default implementation.</summary>
5816 class DefaultAttributeValueFormatter : AttributeValueFormatter {
5817 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5819 returnvalue = "(" + MDocUpdater.GetDocTypeFullName (type.Reference) + ") " + v.ToString ();
5824 /// <summary>Flags enum formatter that assumes powers of two values.</summary>
5825 /// <remarks>As described here: https://msdn.microsoft.com/en-us/library/vstudio/ms229062(v=vs.100).aspx</remarks>
5826 class StandardFlagsEnumFormatter : AttributeValueFormatter {
5827 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5829 TypeReference valueType = type.Reference;
5830 TypeDefinition valueDef = type.Definition;
5831 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
5833 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5834 var values = MDocUpdater.GetEnumerationValues (valueDef);
5835 long c = MDocUpdater.ToInt64 (v);
5836 returnvalue = string.Join (" | ",
5837 (from i in values.Keys
5838 where (c & i) == i && i != 0
5839 select typename + "." + values [i])
5840 .DefaultIfEmpty (c.ToString ()).ToArray ());
5850 /// <summary>A custom formatter for the ObjCRuntime.Platform enumeration.</summary>
5851 class ApplePlatformEnumFormatter : AttributeValueFormatter {
5852 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5854 TypeReference valueType = type.Reference;
5855 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5856 TypeDefinition valueDef = type.Definition;
5857 if (typename.Contains ("ObjCRuntime.Platform") && valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
5859 var values = MDocUpdater.GetEnumerationValues (valueDef);
5860 long c = MDocUpdater.ToInt64 (v);
5862 returnvalue = Format (c, values, typename);
5870 string Format (long c, IDictionary<long, string> values, string typename)
5872 int iosarch, iosmajor, iosminor, iossubminor;
5873 int macarch, macmajor, macminor, macsubminor;
5874 GetEncodingiOS (c, out iosarch, out iosmajor, out iosminor, out iossubminor);
5875 GetEncodingMac ((ulong)c, out macarch, out macmajor, out macminor, out macsubminor);
5877 if (iosmajor == 0 & iosminor == 0 && iossubminor == 0) {
5878 return FormatValues ("Mac", macarch, macmajor, macminor, macsubminor);
5881 if (macmajor == 0 & macminor == 0 && macsubminor == 0) {
5882 return FormatValues ("iOS", iosarch, iosmajor, iosminor, iossubminor);
5885 return string.Format ("(Platform){0}", c);
5888 string FormatValues (string plat, int arch, int major, int minor, int subminor)
5890 string archstring = "";
5899 return string.Format ("Platform.{4}_{0}_{1}{2} | Platform.{4}_Arch{3}",
5902 subminor == 0 ? "" : "_" + subminor.ToString (),
5908 void GetEncodingiOS (long entireLong, out int archindex, out int major, out int minor, out int subminor)
5910 long lowerBits = entireLong & 0xffffffff;
5911 int lowerBitsAsInt = (int) lowerBits;
5912 GetEncoding (lowerBitsAsInt, out archindex, out major, out minor, out subminor);
5915 void GetEncodingMac (ulong entireLong, out int archindex, out int major, out int minor, out int subminor)
5917 ulong higherBits = entireLong & 0xffffffff00000000;
5918 int higherBitsAsInt = (int) ((higherBits) >> 32);
5919 GetEncoding (higherBitsAsInt, out archindex, out major, out minor, out subminor);
5922 void GetEncoding (Int32 encodedBits, out int archindex, out int major, out int minor, out int subminor)
5924 // format is AAJJNNSS
5925 archindex = (int)((encodedBits & 0xFF000000) >> 24);
5926 major = (int)((encodedBits & 0x00FF0000) >> 16);
5927 minor = (int)((encodedBits & 0x0000FF00) >> 8);
5928 subminor = (int)((encodedBits & 0x000000FF) >> 0);