1 // Updater program for syncing Mono's ECMA-style documentation files
3 // By Joshua Tauberer <tauberer@for.net>
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Collections.ObjectModel;
9 using System.Diagnostics;
10 using System.Globalization;
15 using System.Xml.XPath;
20 using MyXmlNodeList = System.Collections.Generic.List<System.Xml.XmlNode>;
21 using StringList = System.Collections.Generic.List<string>;
22 using StringToStringMap = System.Collections.Generic.Dictionary<string, string>;
23 using StringToXmlNodeMap = System.Collections.Generic.Dictionary<string, System.Xml.XmlNode>;
25 namespace Mono.Documentation {
26 static class NativeTypeManager {
28 static Dictionary<string, string> toNativeType = new Dictionary<string,string>(){
32 {"System.Int32", "System.nint"},
35 {"System.UInt32", "System.nuint"},
38 {"System.Single", "System.nfloat"},
39 {"SizeF", "CoreGraphics.CGSize"},
40 {"System.Drawing.SizeF", "CoreGraphics.CGSize"},
41 {"PointF", "CoreGraphics.CGPoint"},
42 {"System.Drawing.PointF", "CoreGraphics.CGPoint"},
43 {"RectangleF", "CoreGraphics.CGRect" },
44 {"System.Drawing.RectangleF", "CoreGraphics.CGRect"}
47 static Dictionary<string, string> fromNativeType = new Dictionary<string,string>(){
50 {"System.nint", "System.Int32"},
52 {"System.nuint", "System.UInt32"},
54 {"System.nfloat", "System.Single"},
55 {"CoreGraphics.CGSize", "System.Drawing.SizeF"},
56 {"CoreGraphics.CGPoint", "System.Drawing.PointF"},
57 {"CoreGraphics.CGRect", "System.Drawing.RectangleF"},
58 {"MonoTouch.CoreGraphics.CGSize", "System.Drawing.SizeF"},
59 {"MonoTouch.CoreGraphics.CGPoint", "System.Drawing.PointF"},
60 {"MonoTouch.CoreGraphics.CGRect", "System.Drawing.RectangleF"}
63 public static string ConvertToNativeType(string typename) {
68 string valueToCompare = StripToComparableType (typename, ref isOut, ref isArray);
70 if (toNativeType.TryGetValue (valueToCompare, out nvalue)) {
82 public static string ConvertFromNativeType(string typename) {
87 string valueToCompare = StripToComparableType (typename, ref isOut, ref isArray);
89 if (fromNativeType.TryGetValue (valueToCompare, out nvalue)) {
98 // it wasn't one of the native types ... just return it
102 static string StripToComparableType (string typename, ref bool isOut, ref bool isArray)
104 string valueToCompare = typename;
105 if (typename.EndsWith ("[]")) {
106 valueToCompare = typename.Substring (0, typename.Length - 2);
109 if (typename.EndsWith ("&")) {
110 valueToCompare = typename.Substring (0, typename.Length - 1);
113 if (typename.Contains ("<")) {
114 // TODO: Need to recursively process generic parameters
116 return valueToCompare;
119 public static string GetTranslatedName(TypeReference t) {
120 string typename = t.FullName;
122 bool isInAssembly = MDocUpdater.IsInAssemblies (t.Module.Name);
123 if (isInAssembly && !typename.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (t)) {
124 string nameWithDropped = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typename);
125 return nameWithDropped;
130 class MDocUpdater : MDocCommand
133 List<AssemblyDefinition> assemblies;
134 readonly DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver();
137 bool show_exceptions;
138 bool no_assembly_versions, ignore_missing_types;
139 ExceptionLocations? exceptions;
141 internal int additions = 0, deletions = 0;
143 List<DocumentationImporter> importers = new List<DocumentationImporter> ();
145 DocumentationEnumerator docEnum;
149 static readonly MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
150 static readonly MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
152 static MemberFormatter[] typeFormatters = new MemberFormatter[]{
153 new CSharpMemberFormatter (),
154 new ILMemberFormatter (),
157 static MemberFormatter[] memberFormatters = new MemberFormatter[]{
158 new CSharpFullMemberFormatter (),
159 new ILFullMemberFormatter (),
162 internal static readonly MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
164 MyXmlNodeList extensionMethods = new MyXmlNodeList ();
166 HashSet<string> forwardedTypes = new HashSet<string> ();
168 public static string droppedNamespace = string.Empty;
170 public static bool HasDroppedNamespace(TypeDefinition forType)
172 return HasDroppedNamespace(forType.Module);
175 public static bool HasDroppedNamespace(MemberReference forMember)
177 return HasDroppedNamespace(forMember.Module);
180 public static bool HasDroppedNamespace(AssemblyDefinition forAssembly)
182 return HasDroppedNamespace(forAssembly.MainModule);
185 public static bool HasDroppedNamespace(ModuleDefinition forModule)
187 return !string.IsNullOrWhiteSpace (droppedNamespace) && droppedAssemblies.Any(da => da == forModule.Name);
191 static List<string> droppedAssemblies = new List<string>();
193 public string PreserveTag { get; set; }
194 public static MDocUpdater Instance { get; private set; }
195 public static bool SwitchingToMagicTypes { get; private set; }
197 public override void Run (IEnumerable<string> args)
200 show_exceptions = DebugOutput;
201 var types = new List<string> ();
202 var p = new OptionSet () {
204 "Delete removed members from the XML files.",
205 v => delete = v != null },
207 "Document potential exceptions that members can generate. {SOURCES} " +
208 "is a comma-separated list of:\n" +
209 " asm Method calls in same assembly\n" +
210 " depasm Method calls in dependent assemblies\n" +
211 " all Record all possible exceptions\n" +
212 " added Modifier; only create <exception/>s\n" +
213 " for NEW types/members\n" +
214 "If nothing is specified, then only exceptions from the member will " +
216 v => exceptions = ParseExceptionLocations (v) },
218 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
221 case "ignore-missing-types":
222 ignore_missing_types = true;
224 case "no-assembly-versions":
225 no_assembly_versions = true;
228 throw new Exception ("Unsupported flag `" + v + "'.");
231 { "fignore-missing-types",
232 "Do not report an error if a --type=TYPE type\nwas not found.",
233 v => ignore_missing_types = v != null },
234 { "fno-assembly-versions",
235 "Do not generate //AssemblyVersion elements.",
236 v => no_assembly_versions = v != null },
238 "Import documentation from {FILE}.",
239 v => AddImporter (v) },
241 "Check for assembly references in {DIRECTORY}.",
242 v => assemblyResolver.AddSearchDirectory (v) },
244 "Ignored for compatibility with update-ecma-xml.",
247 "Root {DIRECTORY} to generate/update documentation.",
250 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
251 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
252 v => assemblyResolver.AddSearchDirectory (Path.GetDirectoryName (v)) },
254 "Manually specify the assembly {VERSION} that new members were added in.",
257 "Only update documentation for {TYPE}.",
258 v => types.Add (v) },
260 "When processing assembly {ASSEMBLY}, strip off leading namespace {PREFIX}:\n" +
261 " e.g. --dropns ASSEMBLY=PREFIX",
263 var parts = v.Split ('=');
264 if (parts.Length != 2) { Console.Error.WriteLine ("Invalid dropns input"); return; }
265 var assembly = Path.GetFileName (parts [0].Trim ());
266 var prefix = parts [1].Trim();
267 droppedAssemblies.Add (assembly);
268 droppedNamespace = prefix;
271 "If the new assembly is switching to 'magic types', then this switch should be defined.",
272 v => SwitchingToMagicTypes = true },
274 "Do not delete members that don't exist in the assembly, but rather mark them as preserved.",
275 v => PreserveTag = "true" },
277 var assemblies = Parse (p, args, "update",
278 "[OPTIONS]+ ASSEMBLIES",
279 "Create or update documentation from ASSEMBLIES.");
280 if (assemblies == null)
282 if (assemblies.Count == 0)
283 Error ("No assemblies specified.");
285 foreach (var dir in assemblies
286 .Where (a => a.Contains (Path.DirectorySeparatorChar))
287 .Select (a => Path.GetDirectoryName (a)))
288 assemblyResolver.AddSearchDirectory (dir);
290 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
293 throw new InvalidOperationException("The --out option is required.");
295 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
297 // Store types that have been forwarded to avoid duplicate generation
298 GatherForwardedTypes ();
300 docEnum = docEnum ?? new DocumentationEnumerator ();
302 // PERFORM THE UPDATES
304 if (types.Count > 0) {
306 DoUpdateTypes (srcPath, types, srcPath);
309 else if (opts.@namespace != null)
310 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
311 Path.Combine (dest_dir, opts.@namespace));
314 DoUpdateAssemblies (srcPath, srcPath);
316 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
318 public static bool IsInAssemblies(string name) {
319 var query = Instance.assemblies.Where (a => a.MainModule.Name == name).ToArray ();
320 return query.Length > 0;
322 void AddImporter (string path)
325 XmlReader r = new XmlTextReader (path);
327 while (r.NodeType != XmlNodeType.Element) {
329 Error ("Unable to read XML file: {0}.", path);
331 if (r.LocalName == "doc") {
332 importers.Add (new MsxdocDocumentationImporter (path));
334 else if (r.LocalName == "Libraries") {
335 var ecmadocs = new XmlTextReader (path);
336 docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
337 importers.Add (new EcmaDocumentationImporter (ecmadocs));
340 Error ("Unsupported XML format within {0}.", path);
343 } catch (Exception e) {
344 Environment.ExitCode = 1;
345 Error ("Could not load XML file: {0}.", e.Message);
349 void GatherForwardedTypes ()
351 foreach (var asm in assemblies)
352 foreach (var type in asm.MainModule.ExportedTypes.Where (t => t.IsForwarder).Select (t => t.FullName))
353 forwardedTypes.Add (type);
356 static ExceptionLocations ParseExceptionLocations (string s)
358 ExceptionLocations loc = ExceptionLocations.Member;
361 foreach (var type in s.Split (',')) {
363 case "added": loc |= ExceptionLocations.AddedMembers; break;
364 case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
365 case "asm": loc |= ExceptionLocations.Assembly; break;
366 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
367 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
373 internal void Warning (string format, params object[] args)
375 Message (TraceLevel.Warning, "mdoc: " + format, args);
378 private AssemblyDefinition LoadAssembly (string name)
380 AssemblyDefinition assembly = null;
382 assembly = AssemblyDefinition.ReadAssembly (name, new ReaderParameters { AssemblyResolver = assemblyResolver });
383 } catch (System.IO.FileNotFoundException) { }
385 if (assembly == null)
386 throw new InvalidOperationException("Assembly " + name + " not found.");
391 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
392 OrderTypeAttributes (element);
393 XmlTextWriter writer = new XmlTextWriter(output);
394 writer.Formatting = Formatting.Indented;
395 writer.Indentation = 2;
396 writer.IndentChar = ' ';
397 element.WriteTo(writer);
401 private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
403 Action<string> creator = file => {
404 using (var writer = OpenWrite (file, mode))
408 MdocFile.UpdateFile (filename, creator);
411 private static void OrderTypeAttributes (XmlElement e)
413 foreach (XmlElement type in e.SelectNodes ("//Type")) {
414 OrderTypeAttributes (type.Attributes);
418 static readonly string[] TypeAttributeOrder = {
419 "Name", "FullName", "FullNameSP", "Maintainer"
422 private static void OrderTypeAttributes (XmlAttributeCollection c)
424 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
425 for (int i = 0; i < c.Count; ++i) {
426 XmlAttribute a = c [i];
427 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
428 if (a.Name == TypeAttributeOrder [j]) {
434 for (int i = attrs.Length-1; i >= 0; --i) {
435 XmlAttribute n = attrs [i];
438 XmlAttribute r = null;
439 for (int j = i+1; j < attrs.Length; ++j) {
440 if (attrs [j] != null) {
448 c.InsertBefore (n, r);
452 private XmlDocument CreateIndexStub()
454 XmlDocument index = new XmlDocument();
456 XmlElement index_root = index.CreateElement("Overview");
457 index.AppendChild(index_root);
459 if (assemblies.Count == 0)
460 throw new Exception ("No assembly");
462 XmlElement index_assemblies = index.CreateElement("Assemblies");
463 index_root.AppendChild(index_assemblies);
465 XmlElement index_remarks = index.CreateElement("Remarks");
466 index_remarks.InnerText = "To be added.";
467 index_root.AppendChild(index_remarks);
469 XmlElement index_copyright = index.CreateElement("Copyright");
470 index_copyright.InnerText = "To be added.";
471 index_root.AppendChild(index_copyright);
473 XmlElement index_types = index.CreateElement("Types");
474 index_root.AppendChild(index_types);
479 private static void WriteNamespaceStub(string ns, string outdir) {
480 XmlDocument index = new XmlDocument();
482 XmlElement index_root = index.CreateElement("Namespace");
483 index.AppendChild(index_root);
485 index_root.SetAttribute("Name", ns);
487 XmlElement index_docs = index.CreateElement("Docs");
488 index_root.AppendChild(index_docs);
490 XmlElement index_summary = index.CreateElement("summary");
491 index_summary.InnerText = "To be added.";
492 index_docs.AppendChild(index_summary);
494 XmlElement index_remarks = index.CreateElement("remarks");
495 index_remarks.InnerText = "To be added.";
496 index_docs.AppendChild(index_remarks);
498 WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
499 writer => WriteXml (index.DocumentElement, writer));
502 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
504 var index = CreateIndexForTypes (dest);
506 var found = new HashSet<string> ();
507 foreach (AssemblyDefinition assembly in assemblies) {
508 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames)) {
509 string relpath = DoUpdateType (type, basepath, dest);
513 found.Add (type.FullName);
518 index.Add (assembly);
526 if (ignore_missing_types)
529 var notFound = from n in typenames where !found.Contains (n) select n;
531 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
534 class IndexForTypes {
540 XmlElement index_types;
541 XmlElement index_assemblies;
543 public IndexForTypes (MDocUpdater app, string indexFile, XmlDocument index)
546 this.indexFile = indexFile;
549 index_types = WriteElement (index.DocumentElement, "Types");
550 index_assemblies = WriteElement (index.DocumentElement, "Assemblies");
553 public void Add (AssemblyDefinition assembly)
555 if (index_assemblies.SelectSingleNode ("Assembly[@Name='" + assembly.Name.Name + "']") != null)
558 app.AddIndexAssembly (assembly, index_assemblies);
561 public void Add (TypeDefinition type)
563 app.AddIndexType (type, index_types);
568 SortIndexEntries (index_types);
569 WriteFile (indexFile, FileMode.Create,
570 writer => WriteXml (index.DocumentElement, writer));
574 IndexForTypes CreateIndexForTypes (string dest)
576 string indexFile = Path.Combine (dest, "index.xml");
577 if (File.Exists (indexFile))
579 return new IndexForTypes (this, indexFile, CreateIndexStub ());
582 /// <summary>Constructs the presumed path to the type's documentation file</summary>
583 /// <returns><c>true</c>, if the type file was found, <c>false</c> otherwise.</returns>
584 /// <param name="result">A typle that contains 1) the 'reltypefile', 2) the 'typefile', and 3) the file info</param>
585 bool TryFindTypeFile(string nsname, string typename, string basepath, out Tuple<string, string, FileInfo> result) {
586 string reltypefile = DocUtils.PathCombine (nsname, typename + ".xml");
587 string typefile = Path.Combine (basepath, reltypefile);
588 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
590 result = new Tuple<string, string, FileInfo> (reltypefile, typefile, file);
595 public string DoUpdateType (TypeDefinition type, string basepath, string dest)
597 if (type.Namespace == null)
598 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
600 if (!IsPublic (type))
603 // Must get the A+B form of the type name.
604 string typename = GetTypeFileName(type);
605 string nsname = DocUtils.GetNamespace (type);
607 // Find the file, if it exists
608 string[] searchLocations = new string[] {
612 if (MDocUpdater.HasDroppedNamespace (type)) {
613 // If dropping namespace, types may have moved into a couple of different places.
614 var newSearchLocations = searchLocations.Union (new string[] {
615 string.Format ("{0}.{1}", droppedNamespace, nsname),
616 nsname.Replace (droppedNamespace + ".", string.Empty),
617 MDocUpdater.droppedNamespace
620 searchLocations = newSearchLocations.ToArray ();
623 string reltypefile="", typefile="";
624 System.IO.FileInfo file = null;
626 foreach (var f in searchLocations) {
627 Tuple<string, string, FileInfo> result;
628 bool fileExists = TryFindTypeFile (f, typename, basepath, out result);
631 reltypefile = result.Item1;
632 typefile = result.Item2;
639 if (file == null || !file.Exists) {
640 // we were not able to find a file, let's use the original type informatio.
641 // so that we create the stub in the right place.
642 Tuple<string, string, FileInfo> result;
643 TryFindTypeFile (nsname, typename, basepath, out result);
645 reltypefile = result.Item1;
646 typefile = result.Item2;
650 string output = null;
653 } else if (dest == "-") {
656 output = Path.Combine (dest, reltypefile);
659 if (file != null && file.Exists) {
661 XmlDocument basefile = new XmlDocument();
663 basefile.Load(typefile);
664 } catch (Exception e) {
665 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
668 DoUpdateType2("Updating", basefile, type, output, false);
671 XmlElement td = StubType(type, output);
675 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
678 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
684 public void DoUpdateNS (string ns, string nspath, string outpath)
686 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
687 AssemblyDefinition assembly = assemblies [0];
689 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
690 XmlDocument basefile = new XmlDocument();
691 string typefile = Path.Combine(nspath, file.Name);
693 basefile.Load(typefile);
694 } catch (Exception e) {
695 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
699 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
700 TypeDefinition type = assembly.GetType(typename);
703 if (!string.IsNullOrWhiteSpace (droppedNamespace)) {
704 string nameWithNs = string.Format ("{0}.{1}", droppedNamespace, typename);
705 type = assembly.GetType (nameWithNs);
707 Warning ("Type no longer in assembly: " + typename);
714 seenTypes[type] = seenTypes;
715 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false);
718 // Stub types not in the directory
719 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
720 if (type.Namespace != ns || seenTypes.ContainsKey(type))
723 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"));
724 if (td == null) continue;
728 private static string GetTypeFileName (TypeReference type)
730 return filenameFormatter.GetName (type);
733 public static string GetTypeFileName (string typename)
735 StringBuilder filename = new StringBuilder (typename.Length);
739 for (int i = 0; i < typename.Length; ++i) {
740 char c = typename [i];
749 filename.Append ('`').Append ((numArgs+1).ToString());
764 return filename.ToString ();
767 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
769 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
770 index_assembly.SetAttribute ("Name", assembly.Name.Name);
771 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
773 AssemblyNameDefinition name = assembly.Name;
774 if (name.HasPublicKey) {
775 XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
776 var key = new StringBuilder (name.PublicKey.Length*3 + 2);
778 foreach (byte b in name.PublicKey)
779 key.AppendFormat ("{0,2:x2} ", b);
781 pubkey.InnerText = key.ToString ();
782 index_assembly.AppendChild (pubkey);
785 if (!string.IsNullOrEmpty (name.Culture)) {
786 XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
787 culture.InnerText = name.Culture;
788 index_assembly.AppendChild (culture);
791 MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
792 parent.AppendChild(index_assembly);
795 private void AddIndexType (TypeDefinition type, XmlElement index_types)
797 string typename = GetTypeFileName(type);
799 // Add namespace and type nodes into the index file as needed
800 string ns = DocUtils.GetNamespace (type);
801 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode ("Namespace[@Name='" + ns + "']");
802 if (nsnode == null) {
803 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
804 nsnode.SetAttribute ("Name", ns);
805 index_types.AppendChild (nsnode);
807 string doc_typename = GetDocTypeName (type);
808 XmlElement typenode = (XmlElement) nsnode.SelectSingleNode ("Type[@Name='" + typename + "']");
809 if (typenode == null) {
810 typenode = index_types.OwnerDocument.CreateElement ("Type");
811 typenode.SetAttribute ("Name", typename);
812 nsnode.AppendChild (typenode);
814 if (typename != doc_typename)
815 typenode.SetAttribute("DisplayName", doc_typename);
817 typenode.RemoveAttribute("DisplayName");
819 typenode.SetAttribute ("Kind", GetTypeKind (type));
822 private void DoUpdateAssemblies (string source, string dest)
824 string indexfile = dest + "/index.xml";
826 if (System.IO.File.Exists(indexfile)) {
827 index = new XmlDocument();
828 index.Load(indexfile);
831 ClearElement(index.DocumentElement, "Assembly");
832 ClearElement(index.DocumentElement, "Attributes");
834 index = CreateIndexStub();
837 string defaultTitle = "Untitled";
838 if (assemblies.Count == 1)
839 defaultTitle = assemblies[0].Name.Name;
840 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
842 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
843 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
844 index_assemblies.RemoveAll ();
847 HashSet<string> goodfiles = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
849 foreach (AssemblyDefinition assm in assemblies) {
850 AddIndexAssembly (assm, index_assemblies);
851 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
854 SortIndexEntries (index_types);
856 CleanupFiles (dest, goodfiles);
857 CleanupIndexTypes (index_types, goodfiles);
858 CleanupExtensions (index_types);
860 WriteFile (indexfile, FileMode.Create,
861 writer => WriteXml(index.DocumentElement, writer));
864 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
866 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
868 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
869 string typename = GetTypeFileName(type);
870 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0 || forwardedTypes.Contains (type.FullName))
873 string reltypepath = DoUpdateType (type, source, dest);
874 if (reltypepath == null)
877 // Add namespace and type nodes into the index file as needed
878 AddIndexType (type, index_types);
880 // Ensure the namespace index file exists
881 string namespaceToUse = type.Namespace;
882 if (HasDroppedNamespace(assembly)) {
883 namespaceToUse = string.Format ("{0}.{1}", droppedNamespace, namespaceToUse);
885 string onsdoc = DocUtils.PathCombine (dest, namespaceToUse + ".xml");
886 string nsdoc = DocUtils.PathCombine (dest, "ns-" + namespaceToUse + ".xml");
887 if (File.Exists (onsdoc)) {
888 File.Move (onsdoc, nsdoc);
891 if (!File.Exists (nsdoc)) {
892 Console.WriteLine("New Namespace File: " + type.Namespace);
893 WriteNamespaceStub(namespaceToUse, dest);
896 goodfiles.Add (reltypepath);
900 private static void SortIndexEntries (XmlElement indexTypes)
902 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
903 XmlNodeComparer c = new AttributeNameComparer ();
904 SortXmlNodes (indexTypes, namespaces, c);
906 for (int i = 0; i < namespaces.Count; ++i)
907 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
910 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
912 MyXmlNodeList l = new MyXmlNodeList (children.Count);
913 for (int i = 0; i < children.Count; ++i)
914 l.Add (children [i]);
916 for (int i = l.Count - 1; i > 0; --i) {
917 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
921 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
923 public abstract int Compare (XmlNode x, XmlNode y);
925 public int Compare (object x, object y)
927 return Compare ((XmlNode) x, (XmlNode) y);
931 class AttributeNameComparer : XmlNodeComparer {
934 public AttributeNameComparer ()
939 public AttributeNameComparer (string attribute)
941 this.attribute = attribute;
944 public override int Compare (XmlNode x, XmlNode y)
946 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
950 class VersionComparer : XmlNodeComparer {
951 public override int Compare (XmlNode x, XmlNode y)
953 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
954 string a = GetVersion (x.InnerText);
955 string b = GetVersion (y.InnerText);
956 return new Version (a).CompareTo (new Version (b));
959 static string GetVersion (string v)
961 int n = v.IndexOf ("x");
964 return v.Substring (0, n-1);
968 private static string GetTypeKind (TypeDefinition type)
971 return "Enumeration";
972 if (type.IsValueType)
974 if (type.IsInterface)
976 if (DocUtils.IsDelegate (type))
978 if (type.IsClass || type.FullName == "System.Enum") // FIXME
980 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
983 public static bool IsPublic (TypeDefinition type)
985 TypeDefinition decl = type;
986 while (decl != null) {
987 if (!(decl.IsPublic || decl.IsNestedPublic ||
988 decl.IsNestedFamily || decl.IsNestedFamily || decl.IsNestedFamilyOrAssembly)) {
991 decl = (TypeDefinition) decl.DeclaringType;
996 private void CleanupFiles (string dest, HashSet<string> goodfiles)
998 // Look for files that no longer correspond to types
999 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
1000 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
1001 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
1002 if (!goodfiles.Contains (relTypeFile)) {
1003 XmlDocument doc = new XmlDocument ();
1004 doc.Load (typefile.FullName);
1005 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
1006 string assemblyName = doc.SelectSingleNode ("/Type/AssemblyInfo/AssemblyName").InnerText;
1007 AssemblyDefinition assembly = assemblies.FirstOrDefault (a => a.Name.Name == assemblyName);
1008 if (e != null && !no_assembly_versions && assembly != null && assemblyName != null && UpdateAssemblyVersions(e, assembly, GetAssemblyVersions(assemblyName), false)) {
1009 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
1010 WriteXml(doc.DocumentElement, writer);
1011 goodfiles.Add (relTypeFile);
1015 if (string.IsNullOrWhiteSpace (PreserveTag)) { // only do this if there was no -preserve
1016 string newname = typefile.FullName + ".remove";
1017 try { System.IO.File.Delete(newname); } catch (Exception) { }
1018 try { typefile.MoveTo(newname); } catch (Exception) { }
1019 Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
1026 private static TextWriter OpenWrite (string path, FileMode mode)
1028 var w = new StreamWriter (
1029 new FileStream (path, mode),
1030 new UTF8Encoding (false)
1036 private string[] GetAssemblyVersions (string assemblyName)
1038 return (from a in assemblies
1039 where a.Name.Name == assemblyName
1040 select GetAssemblyVersion (a)).ToArray ();
1043 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
1045 // Look for type nodes that no longer correspond to types
1046 MyXmlNodeList remove = new MyXmlNodeList ();
1047 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
1048 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
1049 if (!goodfiles.Contains (fulltypename)) {
1050 remove.Add (typenode);
1053 foreach (XmlNode n in remove)
1054 n.ParentNode.RemoveChild (n);
1057 private void CleanupExtensions (XmlElement index_types)
1059 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
1060 if (extensionMethods.Count == 0) {
1063 index_types.SelectSingleNode ("/Overview").RemoveChild (e);
1067 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
1068 index_types.SelectSingleNode ("/Overview").AppendChild (e);
1072 extensionMethods.Sort (DefaultExtensionMethodComparer);
1073 foreach (XmlNode m in extensionMethods) {
1074 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
1078 class ExtensionMethodComparer : XmlNodeComparer {
1079 public override int Compare (XmlNode x, XmlNode y)
1081 XmlNode xLink = x.SelectSingleNode ("Member/Link");
1082 XmlNode yLink = y.SelectSingleNode ("Member/Link");
1084 int n = xLink.Attributes ["Type"].Value.CompareTo (
1085 yLink.Attributes ["Type"].Value);
1088 n = xLink.Attributes ["Member"].Value.CompareTo (
1089 yLink.Attributes ["Member"].Value);
1090 if (n == 0 && !object.ReferenceEquals (x, y))
1091 throw new InvalidOperationException ("Duplicate extension method found!");
1096 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
1098 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince)
1100 Console.WriteLine(message + ": " + type.FullName);
1102 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
1104 // Update type metadata
1105 UpdateType(basefile.DocumentElement, type);
1107 // Update existing members. Delete member nodes that no longer should be there,
1108 // and remember what members are already documented so we don't add them again.
1110 MyXmlNodeList todelete = new MyXmlNodeList ();
1112 foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
1113 XmlElement oldmember = info.Node;
1114 MemberReference oldmember2 = info.Member;
1115 string sig = oldmember2 != null ? memberFormatters [0].GetDeclaration (oldmember2) : null;
1117 // Interface implementations and overrides are deleted from the docs
1118 // unless the overrides option is given.
1119 if (oldmember2 != null && sig == null)
1122 // Deleted (or signature changed)
1123 if (oldmember2 == null) {
1124 if (!no_assembly_versions && UpdateAssemblyVersions (oldmember, type.Module.Assembly, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
1127 DeleteMember ("Member Removed", output, oldmember, todelete, type);
1132 if (seenmembers.ContainsKey (sig)) {
1133 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
1134 // ignore, already seen
1136 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
1137 DeleteMember ("Duplicate Member Found", output, oldmember, todelete, type);
1139 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
1143 // Update signature information
1146 // get all apistyles of sig from info.Node
1147 var styles = oldmember.GetElementsByTagName ("MemberSignature").Cast<XmlElement> ()
1148 .Where (x => x.GetAttribute ("Language") == "C#" && !seenmembers.ContainsKey(x.GetAttribute("Value")))
1149 .Select (x => x.GetAttribute ("Value"));
1151 foreach (var stylesig in styles) {
1152 seenmembers.Add (stylesig, oldmember);
1155 foreach (XmlElement oldmember in todelete)
1156 oldmember.ParentNode.RemoveChild (oldmember);
1159 if (!DocUtils.IsDelegate (type)) {
1160 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
1161 var typemembers = type.GetMembers()
1163 if (m is TypeDefinition) return false;
1164 string sig = memberFormatters [0].GetDeclaration (m);
1165 if (sig == null) return false;
1166 if (seenmembers.ContainsKey(sig)) return false;
1168 // Verify that the member isn't an explicitly implemented
1169 // member of an internal interface, in which case we shouldn't return true.
1170 MethodDefinition methdef = null;
1171 if (m is MethodDefinition)
1172 methdef = m as MethodDefinition;
1173 else if (m is PropertyDefinition) {
1174 var prop = m as PropertyDefinition;
1175 methdef = prop.GetMethod ?? prop.SetMethod;
1178 if (methdef != null) {
1179 TypeReference iface;
1180 MethodReference imethod;
1182 if (methdef.Overrides.Count == 1) {
1183 DocUtils.GetInfoForExplicitlyImplementedMethod (methdef, out iface, out imethod);
1184 if (!IsPublic (iface.Resolve ())) return false;
1191 foreach (MemberReference m in typemembers) {
1192 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
1193 if (mm == null) continue;
1195 if (MDocUpdater.SwitchingToMagicTypes) {
1196 // this is a unified style API that obviously doesn't exist in the classic API. Let's mark
1197 // it with apistyle="unified", so that it's not displayed for classic style APIs
1198 mm.SetAttribute ("apistyle", "unified");
1201 members.AppendChild( mm );
1203 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
1208 // Import code snippets from files
1209 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
1210 if (!(code is XmlElement)) continue;
1211 string file = ((XmlElement)code).GetAttribute("src");
1212 string lang = ((XmlElement)code).GetAttribute("lang");
1214 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
1216 code.InnerText = src;
1220 if (insertSince && since != null) {
1221 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
1222 docs.AppendChild (CreateSinceNode (basefile));
1226 XmlElement d = basefile.DocumentElement ["Docs"];
1227 XmlElement m = basefile.DocumentElement ["Members"];
1228 if (d != null && m != null)
1229 basefile.DocumentElement.InsertBefore (
1230 basefile.DocumentElement.RemoveChild (d), m);
1231 SortTypeMembers (m);
1235 WriteXml(basefile.DocumentElement, Console.Out);
1237 FileInfo file = new FileInfo (output);
1238 if (!file.Directory.Exists) {
1239 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
1240 file.Directory.Create ();
1242 WriteFile (output, FileMode.Create,
1243 writer => WriteXml(basefile.DocumentElement, writer));
1247 private string GetCodeSource (string lang, string file)
1250 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
1251 // Grab the specified region
1252 string region = "#region " + file.Substring (anchorStart + 4);
1253 file = file.Substring (0, anchorStart + 3);
1255 using (StreamReader reader = new StreamReader (file)) {
1257 StringBuilder src = new StringBuilder ();
1259 while ((line = reader.ReadLine ()) != null) {
1260 if (line.Trim() == region) {
1261 indent = line.IndexOf (region);
1264 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
1269 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
1272 return src.ToString ();
1274 } catch (Exception e) {
1275 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
1276 file, region, show_exceptions ? e.ToString () : e.Message);
1281 using (StreamReader reader = new StreamReader (file))
1282 return reader.ReadToEnd ();
1283 } catch (Exception e) {
1284 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
1289 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete, TypeDefinition type)
1291 string format = output != null
1292 ? "{0}: File='{1}'; Signature='{4}'"
1293 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1297 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1298 member.Attributes ["MemberName"].Value,
1299 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
1300 if (!delete && MemberDocsHaveUserContent (member)) {
1301 Warning ("Member deletions must be enabled with the --delete option.");
1302 } else if (HasDroppedNamespace (type)) {
1303 // if we're dropping the namespace, add the "classic style"
1304 var existingAttribute = member.Attributes ["apistyle"];
1305 if (existingAttribute != null) {
1306 existingAttribute.Value = "classic";
1308 // add the attribute and do not remove
1309 XmlAttribute apistyleAttr = member.OwnerDocument.CreateAttribute ("apistyle");
1311 apistyleAttr.Value = "classic";
1313 member.Attributes.Append (apistyleAttr);
1315 } else if (!HasDroppedNamespace (type) && member.Attributes ["apistyle"] != null && member.Attributes ["apistyle"].Value == "unified") {
1316 // do nothing if there's an apistyle=new attribute and we haven't dropped the namespace
1317 } else if (!string.IsNullOrWhiteSpace (PreserveTag)) {
1320 todelete.Add (member);
1325 class MemberComparer : XmlNodeComparer {
1326 public override int Compare (XmlNode x, XmlNode y)
1329 string xMemberName = x.Attributes ["MemberName"].Value;
1330 string yMemberName = y.Attributes ["MemberName"].Value;
1332 // generic methods *end* with '>'
1333 // it's possible for explicitly implemented generic interfaces to
1334 // contain <...> without being a generic method
1335 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1336 (r = xMemberName.CompareTo (yMemberName)) != 0)
1340 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1341 xMemberName = xMemberName.Substring (0, lt);
1342 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1343 yMemberName = yMemberName.Substring (0, lt);
1344 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1347 // if @MemberName matches, then it's either two different types of
1348 // members sharing the same name, e.g. field & property, or it's an
1349 // overloaded method.
1350 // for different type, sort based on MemberType value.
1351 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1352 y.SelectSingleNode ("MemberType").InnerText);
1356 // same type -- must be an overloaded method. Sort based on type
1357 // parameter count, then parameter count, then by the parameter
1359 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1360 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1361 if (xTypeParams.Count != yTypeParams.Count)
1362 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1363 for (int i = 0; i < xTypeParams.Count; ++i) {
1364 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1365 yTypeParams [i].Attributes ["Name"].Value);
1370 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1371 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1372 if (xParams.Count != yParams.Count)
1373 return xParams.Count <= yParams.Count ? -1 : 1;
1374 for (int i = 0; i < xParams.Count; ++i) {
1375 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1376 yParams [i].Attributes ["Type"].Value);
1380 // all parameters match, but return value might not match if it was
1381 // changed between one version and another.
1382 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1383 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1384 if (xReturn != null && yReturn != null) {
1385 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1394 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1396 private static void SortTypeMembers (XmlNode members)
1398 if (members == null)
1400 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1403 private static bool MemberDocsHaveUserContent (XmlNode e)
1405 e = (XmlElement)e.SelectSingleNode("Docs");
1406 if (e == null) return false;
1407 foreach (XmlElement d in e.SelectNodes("*"))
1408 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1413 // UPDATE HELPER FUNCTIONS
1415 // CREATE A STUB DOCUMENTATION FILE
1417 public XmlElement StubType (TypeDefinition type, string output)
1419 string typesig = typeFormatters [0].GetDeclaration (type);
1420 if (typesig == null) return null; // not publicly visible
1422 XmlDocument doc = new XmlDocument();
1423 XmlElement root = doc.CreateElement("Type");
1424 doc.AppendChild (root);
1426 DoUpdateType2 ("New Type", doc, type, output, true);
1431 private XmlElement CreateSinceNode (XmlDocument doc)
1433 XmlElement s = doc.CreateElement ("since");
1434 s.SetAttribute ("version", since);
1438 // STUBBING/UPDATING FUNCTIONS
1440 public void UpdateType (XmlElement root, TypeDefinition type)
1442 root.SetAttribute("Name", GetDocTypeName (type));
1443 root.SetAttribute("FullName", GetDocTypeFullName (type));
1445 foreach (MemberFormatter f in typeFormatters) {
1446 string element = "TypeSignature[@Language='" + f.Language + "']";
1447 string valueToUse = f.GetDeclaration (type);
1450 root.SelectNodes (element).Cast<XmlElement> ().ToArray (),
1451 x => x.GetAttribute ("Value") == valueToUse,
1452 x => x.SetAttribute ("Value", valueToUse),
1454 var node = WriteElementAttribute (root, element, "Language", f.Language, forceNewElement: true);
1455 var newnode = WriteElementAttribute (root, node, "Value", valueToUse);
1461 string assemblyInfoNodeFilter = MDocUpdater.HasDroppedNamespace (type) ? "[@apistyle='unified']" : "[not(@apistyle) or @apistyle='classic']";
1464 root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ().ToArray (),
1465 x => x.SelectSingleNode("AssemblyName").InnerText == type.Module.Assembly.Name.Name,
1466 x => WriteElementText(x, "AssemblyName", type.Module.Assembly.Name.Name),
1468 XmlElement ass = WriteElement(root, "AssemblyInfo", forceNewElement:true);
1470 if (MDocUpdater.HasDroppedNamespace (type)) ass.SetAttribute ("apistyle", "unified");
1478 foreach(var ass in root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ())
1480 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1481 if (!no_assembly_versions) {
1482 UpdateAssemblyVersions (root, type, true);
1485 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1486 foreach (var version in versions)
1487 ass.RemoveChild (version);
1489 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1490 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1492 ClearElement(ass, "AssemblyCulture");
1495 // Why-oh-why do we put assembly attributes in each type file?
1496 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1497 // since they're outdated in current docs, and a waste of space.
1498 //MakeAttributes(ass, type.Assembly, true);
1499 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1500 if (assattrs != null)
1501 ass.RemoveChild(assattrs);
1503 NormalizeWhitespace(ass);
1506 if (type.IsGenericType ()) {
1507 MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
1509 ClearElement(root, "TypeParameters");
1512 if (type.BaseType != null) {
1513 XmlElement basenode = WriteElement(root, "Base");
1515 string basetypename = GetDocTypeFullName (type.BaseType);
1516 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1517 WriteElementText(root, "Base/BaseTypeName", basetypename);
1519 // Document how this type instantiates the generic parameters of its base type
1520 TypeReference origBase = type.BaseType.GetElementType ();
1521 if (origBase.IsGenericType ()) {
1522 ClearElement(basenode, "BaseTypeArguments");
1523 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1524 IList<TypeReference> baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1525 IList<GenericParameter> baseGenParams = origBase.GenericParameters;
1526 if (baseGenArgs.Count != baseGenParams.Count)
1527 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1528 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1529 GenericParameter param = baseGenParams [i];
1530 TypeReference value = baseGenArgs [i];
1532 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1533 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1534 bta.AppendChild(arg);
1535 arg.SetAttribute ("TypeParamName", param.Name);
1536 arg.InnerText = GetDocTypeFullName (value);
1540 ClearElement(root, "Base");
1543 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1544 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1545 List<string> interface_names = userInterfaces
1546 .Select (iface => GetDocTypeFullName (iface))
1550 XmlElement interfaces = WriteElement(root, "Interfaces");
1551 interfaces.RemoveAll();
1552 foreach (string iname in interface_names) {
1553 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1554 interfaces.AppendChild(iface);
1555 WriteElementText(iface, "InterfaceName", iname);
1558 ClearElement(root, "Interfaces");
1561 MakeAttributes (root, GetCustomAttributes (type), type);
1563 if (DocUtils.IsDelegate (type)) {
1564 MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
1565 var member = type.GetMethod ("Invoke");
1566 MakeParameters(root, member, member.Parameters);
1567 MakeReturnValue(root, member);
1570 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1571 MakeDocNode (typeInfo);
1573 if (!DocUtils.IsDelegate (type))
1574 WriteElement (root, "Members");
1576 OrderTypeNodes (root, root.ChildNodes);
1577 NormalizeWhitespace(root);
1580 static readonly string[] TypeNodeOrder = {
1584 "ThreadingSafetyStatement",
1585 "ThreadSafetyStatement",
1597 static void OrderTypeNodes (XmlNode member, XmlNodeList children)
1599 ReorderNodes (member, children, TypeNodeOrder);
1602 internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1604 List<T> l = new List<T> (list);
1609 private void UpdateMember (DocsNodeInfo info)
1611 XmlElement me = (XmlElement) info.Node;
1612 MemberReference mi = info.Member;
1614 foreach (MemberFormatter f in memberFormatters) {
1615 string element = "MemberSignature[@Language='" + f.Language + "']";
1617 var valueToUse = f.GetDeclaration (mi);
1620 me.SelectNodes (element).Cast<XmlElement> ().ToArray(),
1621 x => x.GetAttribute("Value") == valueToUse,
1622 x => x.SetAttribute ("Value", valueToUse),
1624 var node = WriteElementAttribute (me, element, "Language", f.Language, forceNewElement:true);
1625 var newNode = WriteElementAttribute (me, node, "Value", valueToUse);
1632 WriteElementText(me, "MemberType", GetMemberType(mi));
1634 if (!no_assembly_versions) {
1635 UpdateAssemblyVersions (me, mi, true);
1638 ClearElement (me, "AssemblyInfo");
1641 MakeAttributes (me, GetCustomAttributes (mi), mi.DeclaringType);
1643 MakeReturnValue(me, mi, MDocUpdater.HasDroppedNamespace(mi));
1644 if (mi is MethodReference) {
1645 MethodReference mb = (MethodReference) mi;
1646 if (mb.IsGenericMethod ())
1647 MakeTypeParameters (me, mb.GenericParameters, mi, MDocUpdater.HasDroppedNamespace(mi));
1649 MakeParameters(me, mi, MDocUpdater.HasDroppedNamespace(mi));
1652 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1653 WriteElementText(me, "MemberValue", fieldValue);
1655 info.Node = WriteElement (me, "Docs");
1657 OrderMemberNodes (me, me.ChildNodes);
1658 UpdateExtensionMethods (me, info);
1661 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, MemberReference member) {
1662 AddXmlNode (relevant, valueMatches, setValue, makeNewNode, member.Module);
1665 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, TypeDefinition type) {
1666 AddXmlNode (relevant, valueMatches, setValue, makeNewNode, type.Module);
1669 /// <summary>Adds an xml node, reusing the node if it's available</summary>
1670 /// <param name="relevant">The existing set of nodes</param>
1671 /// <param name="valueMatches">Checks to see if the node's value matches what you're trying to write.</param>
1672 /// <param name="setValue">Sets the node's value</param>
1673 /// <param name="makeNewNode">Creates a new node, if valueMatches returns false.</param>
1674 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, ModuleDefinition module)
1676 bool shouldDuplicate = MDocUpdater.HasDroppedNamespace (module);
1677 var styleToUse = shouldDuplicate ? ApiStyle.Unified : ApiStyle.Classic;
1678 var existing = relevant;
1680 bool addedOldApiStyle = false;
1682 if (shouldDuplicate) {
1683 existing = existing.Where (n => n.HasApiStyle (styleToUse)).ToArray ();
1684 foreach (var n in relevant.Where (n => n.DoesNotHaveApiStyle (styleToUse))) {
1685 if (valueMatches (n)) {
1689 n.AddApiStyle (ApiStyle.Classic);
1690 addedOldApiStyle = true;
1695 if (!existing.Any ()) {
1696 var newNode = makeNewNode ();
1697 if (shouldDuplicate && addedOldApiStyle) {
1698 newNode.AddApiStyle (ApiStyle.Unified);
1702 var itemToReuse = existing.First ();
1703 setValue (itemToReuse);
1705 if (shouldDuplicate && addedOldApiStyle) {
1706 itemToReuse.AddApiStyle (styleToUse);
1712 static readonly string[] MemberNodeOrder = {
1727 static void OrderMemberNodes (XmlNode member, XmlNodeList children)
1729 ReorderNodes (member, children, MemberNodeOrder);
1732 static void ReorderNodes (XmlNode node, XmlNodeList children, string[] ordering)
1734 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1735 for (int i = 0; i < ordering.Length; ++i) {
1736 for (int j = 0; j < children.Count; ++j) {
1737 XmlNode c = children [j];
1738 if (c.Name == ordering [i]) {
1739 newChildren.Add (c);
1743 if (newChildren.Count >= 0)
1744 node.PrependChild ((XmlNode) newChildren [0]);
1745 for (int i = 1; i < newChildren.Count; ++i) {
1746 XmlNode prev = (XmlNode) newChildren [i-1];
1747 XmlNode cur = (XmlNode) newChildren [i];
1748 node.RemoveChild (cur);
1749 node.InsertAfter (cur, prev);
1753 IEnumerable<string> GetCustomAttributes (MemberReference mi)
1755 IEnumerable<string> attrs = Enumerable.Empty<string>();
1757 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1759 attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1761 PropertyDefinition pd = mi as PropertyDefinition;
1763 if (pd.GetMethod != null)
1764 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1765 if (pd.SetMethod != null)
1766 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1769 EventDefinition ed = mi as EventDefinition;
1771 if (ed.AddMethod != null)
1772 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1773 if (ed.RemoveMethod != null)
1774 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1780 IEnumerable<string> GetCustomAttributes (IList<CustomAttribute> attributes, string prefix)
1782 foreach (CustomAttribute attribute in attributes.OrderBy (ca => ca.AttributeType.FullName)) {
1784 TypeDefinition attrType = attribute.AttributeType as TypeDefinition;
1785 if (attrType != null && !IsPublic (attrType))
1787 if (slashdocFormatter.GetName (attribute.AttributeType) == null)
1790 if (Array.IndexOf (IgnorableAttributes, attribute.AttributeType.FullName) >= 0)
1793 StringList fields = new StringList ();
1795 for (int i = 0; i < attribute.ConstructorArguments.Count; ++i) {
1796 CustomAttributeArgument argument = attribute.ConstructorArguments [i];
1797 fields.Add (MakeAttributesValueString (
1802 (from namedArg in attribute.Fields
1803 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value })
1805 (from namedArg in attribute.Properties
1806 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value }))
1807 .OrderBy (v => v.Name);
1808 foreach (var d in namedArgs)
1809 fields.Add (string.Format ("{0}={1}", d.Name,
1810 MakeAttributesValueString (d.Value, d.Type)));
1812 string a2 = String.Join(", ", fields.ToArray ());
1813 if (a2 != "") a2 = "(" + a2 + ")";
1815 string name = attribute.GetDeclaringType();
1816 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1817 yield return prefix + name + a2;
1821 static readonly string[] ValidExtensionMembers = {
1830 static readonly string[] ValidExtensionDocMembers = {
1836 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1838 MethodDefinition me = info.Member as MethodDefinition;
1841 if (info.Parameters.Count < 1)
1843 if (!DocUtils.IsExtensionMethod (me))
1846 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1847 XmlNode member = e.CloneNode (true);
1848 em.AppendChild (member);
1849 RemoveExcept (member, ValidExtensionMembers);
1850 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1851 WriteElementText (member, "MemberType", "ExtensionMethod");
1852 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1853 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1854 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1855 member.AppendChild (link);
1856 AddTargets (em, info);
1858 extensionMethods.Add (em);
1861 private static void RemoveExcept (XmlNode node, string[] except)
1865 MyXmlNodeList remove = null;
1866 foreach (XmlNode n in node.ChildNodes) {
1867 if (Array.BinarySearch (except, n.Name) < 0) {
1869 remove = new MyXmlNodeList ();
1874 foreach (XmlNode n in remove)
1875 node.RemoveChild (n);
1878 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1880 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1881 member.PrependChild (targets);
1882 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1883 AppendElementAttributeText (targets, "Target", "Type",
1884 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1887 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1888 IList<TypeReference> constraints = gp.Constraints;
1889 if (constraints.Count == 0)
1890 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1892 foreach (TypeReference c in constraints)
1893 AppendElementAttributeText(targets, "Target", "Type",
1894 slashdocFormatter.GetDeclaration (c));
1898 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1901 TypeDefinition type = field.DeclaringType.Resolve ();
1902 if (type != null && type.IsEnum) return false;
1904 if (type != null && type.IsGenericType ()) return false;
1905 if (!field.HasConstant)
1907 if (field.IsLiteral) {
1908 object val = field.Constant;
1909 if (val == null) value = "null";
1910 else if (val is Enum) value = val.ToString();
1911 else if (val is IFormattable) {
1912 value = ((IFormattable)val).ToString();
1914 value = "\"" + value + "\"";
1916 if (value != null && value != "")
1922 // XML HELPER FUNCTIONS
1924 internal static XmlElement WriteElement(XmlNode parent, string element, bool forceNewElement = false) {
1925 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1926 if (ret == null || forceNewElement) {
1927 string[] path = element.Split('/');
1928 foreach (string p in path) {
1929 ret = (XmlElement)parent.SelectSingleNode(p);
1930 if (ret == null || forceNewElement) {
1932 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1933 ename = ename.Substring(0, ename.IndexOf('['));
1934 ret = parent.OwnerDocument.CreateElement(ename);
1935 parent.AppendChild(ret);
1944 private static XmlElement WriteElementText(XmlNode parent, string element, string value, bool forceNewElement = false) {
1945 XmlElement node = WriteElement(parent, element, forceNewElement: forceNewElement);
1946 node.InnerText = value;
1950 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1952 XmlElement n = parent.OwnerDocument.CreateElement (element);
1953 parent.AppendChild (n);
1954 n.InnerText = value;
1958 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1960 XmlElement n = parent.OwnerDocument.CreateElement (element);
1961 parent.AppendChild (n);
1962 n.SetAttribute (attribute, value);
1966 internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
1968 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1969 dest.AppendChild (copy);
1973 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1974 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1977 node = WriteElement(parent, element);
1978 node.InnerText = value;
1980 private static XmlElement WriteElementAttribute(XmlElement parent, string element, string attribute, string value, bool forceNewElement = false) {
1981 XmlElement node = WriteElement(parent, element, forceNewElement:forceNewElement);
1982 return WriteElementAttribute (parent, node, attribute, value);
1984 private static XmlElement WriteElementAttribute(XmlElement parent, XmlElement node, string attribute, string value) {
1985 if (node.GetAttribute (attribute) != value) {
1986 node.SetAttribute (attribute, value);
1990 internal static void ClearElement(XmlElement parent, string name) {
1991 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1993 parent.RemoveChild(node);
1996 // DOCUMENTATION HELPER FUNCTIONS
1998 private void MakeDocNode (DocsNodeInfo info)
2000 List<GenericParameter> genericParams = info.GenericParameters;
2001 IList<ParameterDefinition> parameters = info.Parameters;
2002 TypeReference returntype = info.ReturnType;
2003 bool returnisreturn = info.ReturnIsReturn;
2004 XmlElement e = info.Node;
2005 bool addremarks = info.AddRemarks;
2007 WriteElementInitialText(e, "summary", "To be added.");
2009 if (parameters != null) {
2010 string[] values = new string [parameters.Count];
2011 for (int i = 0; i < values.Length; ++i)
2012 values [i] = parameters [i].Name;
2013 UpdateParameters (e, "param", values);
2016 if (genericParams != null) {
2017 string[] values = new string [genericParams.Count];
2018 for (int i = 0; i < values.Length; ++i)
2019 values [i] = genericParams [i].Name;
2020 UpdateParameters (e, "typeparam", values);
2023 string retnodename = null;
2024 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
2025 retnodename = returnisreturn ? "returns" : "value";
2026 string retnodename_other = !returnisreturn ? "returns" : "value";
2028 // If it has a returns node instead of a value node, change its name.
2029 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
2030 if (retother != null) {
2031 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
2032 foreach (XmlNode node in retother)
2033 retnode.AppendChild(node.CloneNode(true));
2034 e.ReplaceChild(retnode, retother);
2036 WriteElementInitialText(e, retnodename, "To be added.");
2039 ClearElement(e, "returns");
2040 ClearElement(e, "value");
2044 WriteElementInitialText(e, "remarks", "To be added.");
2046 if (exceptions.HasValue && info.Member != null &&
2047 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
2048 UpdateExceptions (e, info.Member);
2051 foreach (DocumentationImporter importer in importers)
2052 importer.ImportDocumentation (info);
2054 OrderDocsNodes (e, e.ChildNodes);
2055 NormalizeWhitespace(e);
2058 static readonly string[] DocsNodeOrder = {
2059 "typeparam", "param", "summary", "returns", "value", "remarks",
2062 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
2064 ReorderNodes (docs, children, DocsNodeOrder);
2068 private void UpdateParameters (XmlElement e, string element, string[] values)
2070 if (values != null) {
2071 XmlNode[] paramnodes = new XmlNode[values.Length];
2073 // Some documentation had param nodes with leading spaces.
2074 foreach (XmlElement paramnode in e.SelectNodes(element)){
2075 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
2078 // If a member has only one parameter, we can track changes to
2079 // the name of the parameter easily.
2080 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
2081 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
2084 bool reinsert = false;
2086 // Pick out existing and still-valid param nodes, and
2087 // create nodes for parameters not in the file.
2088 Hashtable seenParams = new Hashtable();
2089 for (int pi = 0; pi < values.Length; pi++) {
2090 string p = values [pi];
2093 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
2094 if (paramnodes[pi] != null) continue;
2096 XmlElement pe = e.OwnerDocument.CreateElement(element);
2097 pe.SetAttribute("name", p);
2098 pe.InnerText = "To be added.";
2099 paramnodes[pi] = pe;
2103 // Remove parameters that no longer exist and check all params are in the right order.
2105 MyXmlNodeList todelete = new MyXmlNodeList ();
2106 foreach (XmlElement paramnode in e.SelectNodes(element)) {
2107 string name = paramnode.GetAttribute("name");
2108 if (!seenParams.ContainsKey(name)) {
2109 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2110 Warning ("The following param node can only be deleted if the --delete option is given: ");
2111 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
2113 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
2114 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2118 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
2119 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2120 e.ParentNode.Attributes ["MemberName"].Value,
2123 Warning ("\tValue={0}", paramnode.OuterXml);
2125 todelete.Add (paramnode);
2130 if ((int)seenParams[name] != idx)
2136 foreach (XmlNode n in todelete) {
2137 n.ParentNode.RemoveChild (n);
2140 // Re-insert the parameter nodes at the top of the doc section.
2142 for (int pi = values.Length-1; pi >= 0; pi--)
2143 e.PrependChild(paramnodes[pi]);
2145 // Clear all existing param nodes
2146 foreach (XmlNode paramnode in e.SelectNodes(element)) {
2147 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2148 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
2149 Console.WriteLine(paramnode.OuterXml);
2151 paramnode.ParentNode.RemoveChild(paramnode);
2157 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
2159 string existingName = pe.GetAttribute ("name");
2160 pe.SetAttribute ("name", newName);
2161 if (existingName == newName)
2163 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
2164 if (paramref.GetAttribute ("name").Trim () == existingName)
2165 paramref.SetAttribute ("name", newName);
2168 class CrefComparer : XmlNodeComparer {
2170 public CrefComparer ()
2174 public override int Compare (XmlNode x, XmlNode y)
2176 string xType = x.Attributes ["cref"].Value;
2177 string yType = y.Attributes ["cref"].Value;
2178 string xNamespace = GetNamespace (xType);
2179 string yNamespace = GetNamespace (yType);
2181 int c = xNamespace.CompareTo (yNamespace);
2184 return xType.CompareTo (yType);
2187 static string GetNamespace (string type)
2189 int n = type.LastIndexOf ('.');
2191 return type.Substring (0, n);
2192 return string.Empty;
2196 private void UpdateExceptions (XmlNode docs, MemberReference member)
2198 string indent = new string (' ', 10);
2199 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
2200 string cref = slashdocFormatter.GetDeclaration (source.Exception);
2201 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
2204 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
2205 e.SetAttribute ("cref", cref);
2206 e.InnerXml = "To be added; from:\n" + indent + "<see cref=\"" +
2207 string.Join ("\" />,\n" + indent + "<see cref=\"",
2208 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
2209 .OrderBy (s => s)) +
2211 docs.AppendChild (e);
2213 SortXmlNodes (docs, docs.SelectNodes ("exception"),
2214 new CrefComparer ());
2217 private static void NormalizeWhitespace(XmlElement e) {
2218 // Remove all text and whitespace nodes from the element so it
2219 // is outputted with nice indentation and no blank lines.
2220 ArrayList deleteNodes = new ArrayList();
2221 foreach (XmlNode n in e)
2222 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2224 foreach (XmlNode n in deleteNodes)
2225 n.ParentNode.RemoveChild(n);
2228 private static bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
2230 TypeDefinition type = member as TypeDefinition;
2232 type = member.DeclaringType as TypeDefinition;
2233 return UpdateAssemblyVersions(root, type.Module.Assembly, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
2236 private static string GetAssemblyVersion (AssemblyDefinition assembly)
2238 return assembly.Name.Version.ToString();
2241 private static bool UpdateAssemblyVersions(XmlElement root, AssemblyDefinition assembly, string[] assemblyVersions, bool add)
2243 XmlElement av = (XmlElement) root.SelectSingleNode ("AssemblyVersions");
2245 // AssemblyVersions is not part of the spec
2246 root.RemoveChild (av);
2249 string oldNodeFilter = "AssemblyInfo[not(@apistyle) or @apistyle='classic']";
2250 string newNodeFilter = "AssemblyInfo[@apistyle='unified']";
2251 string thisNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? newNodeFilter : oldNodeFilter;
2252 string thatNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? oldNodeFilter : newNodeFilter;
2254 XmlElement e = (XmlElement) root.SelectSingleNode (thisNodeFilter);
2256 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2258 if (MDocUpdater.HasDroppedNamespace (assembly)) {
2259 e.SetAttribute ("apistyle", "unified");
2262 root.AppendChild(e);
2265 var thatNode = (XmlElement) root.SelectSingleNode (thatNodeFilter);
2266 if (MDocUpdater.HasDroppedNamespace (assembly) && thatNode != null) {
2267 // there's a classic node, we should add apistyles
2268 e.SetAttribute ("apistyle", "unified");
2269 thatNode.SetAttribute ("apistyle", "classic");
2272 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
2273 .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
2275 // matches.Count > 0 && add: ignore -- already present
2276 if (matches.Count > 0 && !add) {
2277 foreach (XmlNode c in matches)
2280 else if (matches.Count == 0 && add) {
2281 foreach (string sv in assemblyVersions) {
2282 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2288 // matches.Count == 0 && !add: ignore -- already not present
2289 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2290 SortXmlNodes (e, avs, new VersionComparer ());
2292 bool anyNodesLeft = avs.Count != 0;
2293 if (!anyNodesLeft) {
2294 e.ParentNode.RemoveChild (e);
2296 return anyNodesLeft;
2299 // FIXME: get TypeReferences instead of string comparison?
2300 private static string[] IgnorableAttributes = {
2301 // Security related attributes
2302 "System.Reflection.AssemblyKeyFileAttribute",
2303 "System.Reflection.AssemblyDelaySignAttribute",
2304 // Present in @RefType
2305 "System.Runtime.InteropServices.OutAttribute",
2306 // For naming the indexer to use when not using indexers
2307 "System.Reflection.DefaultMemberAttribute",
2308 // for decimal constants
2309 "System.Runtime.CompilerServices.DecimalConstantAttribute",
2310 // compiler generated code
2311 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
2312 // more compiler generated code, e.g. iterator methods
2313 "System.Diagnostics.DebuggerHiddenAttribute",
2314 "System.Runtime.CompilerServices.FixedBufferAttribute",
2315 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
2316 // extension methods
2317 "System.Runtime.CompilerServices.ExtensionAttribute",
2318 // Used to differentiate 'object' from C#4 'dynamic'
2319 "System.Runtime.CompilerServices.DynamicAttribute",
2322 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes, TypeReference t=null)
2324 if (!attributes.Any ()) {
2325 ClearElement (root, "Attributes");
2329 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2333 e = root.OwnerDocument.CreateElement("Attributes");
2335 foreach (string attribute in attributes) {
2336 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2339 WriteElementText(ae, "AttributeName", attribute);
2342 if (e.ParentNode == null)
2343 root.AppendChild(e);
2345 NormalizeWhitespace(e);
2348 public static string MakeAttributesValueString (object v, TypeReference valueType)
2352 if (valueType.FullName == "System.Type") {
2353 var vTypeRef = v as TypeReference;
2354 if (vTypeRef != null)
2355 return "typeof(" + NativeTypeManager.GetTranslatedName (vTypeRef) + ")"; // TODO: drop NS handling
2357 return "typeof(" + v.ToString () + ")";
2359 if (valueType.FullName == "System.String")
2360 return "\"" + v.ToString () + "\"";
2361 if (valueType.FullName == "System.Char")
2362 return "'" + v.ToString () + "'";
2364 return (bool)v ? "true" : "false";
2365 TypeDefinition valueDef = valueType.Resolve ();
2366 if (valueDef == null || !valueDef.IsEnum)
2367 return v.ToString ();
2368 string typename = GetDocTypeFullName (valueType);
2369 var values = GetEnumerationValues (valueDef);
2370 long c = ToInt64 (v);
2371 if (values.ContainsKey (c))
2372 return typename + "." + values [c];
2373 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
2374 return string.Join (" | ",
2375 (from i in values.Keys
2377 select typename + "." + values [i])
2378 .DefaultIfEmpty (v.ToString ()).ToArray ());
2380 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
2383 private static Dictionary<long, string> GetEnumerationValues (TypeDefinition type)
2385 var values = new Dictionary<long, string> ();
2387 (from f in type.Fields
2388 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
2390 values [ToInt64 (f.Constant)] = f.Name;
2395 static long ToInt64 (object value)
2398 return (long) (ulong) value;
2399 return Convert.ToInt64 (value);
2402 private void MakeParameters (XmlElement root, MemberReference member, IList<ParameterDefinition> parameters, bool shouldDuplicateWithNew=false)
2404 XmlElement e = WriteElement(root, "Parameters");
2407 foreach (ParameterDefinition p in parameters) {
2411 var ptype = GetDocParameterType (p.ParameterType);
2412 var newPType = ptype;
2414 if (MDocUpdater.SwitchingToMagicTypes) {
2415 newPType = NativeTypeManager.ConvertFromNativeType (ptype);
2418 // now find the existing node, if it's there so we can reuse it.
2419 var nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2420 .Cast<XmlElement> ().Where (x => x.GetAttribute ("Name") == p.Name)
2423 if (nodes.Count () == 0) {
2424 // wasn't found, let's make sure it wasn't just cause the param name was changed
2425 nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2426 .Cast<XmlElement> ()
2427 .Skip (i) // this makes sure we don't inadvertently "reuse" nodes when adding new ones
2428 .Where (x => x.GetAttribute ("Name") != p.Name && (x.GetAttribute ("Type") == ptype || x.GetAttribute ("Type") == newPType))
2429 .Take(1) // there might be more than one that meets this parameter ... only take the first.
2434 x => x.GetAttribute ("Type") == ptype,
2435 x => x.SetAttribute ("Type", ptype),
2437 pe = root.OwnerDocument.CreateElement ("Parameter");
2440 pe.SetAttribute ("Name", p.Name);
2441 pe.SetAttribute ("Type", ptype);
2442 if (p.ParameterType is ByReferenceType) {
2444 pe.SetAttribute ("RefType", "out");
2446 pe.SetAttribute ("RefType", "ref");
2449 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
2458 private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams, MemberReference member, bool shouldDuplicateWithNew)
2460 if (typeParams == null || typeParams.Count == 0) {
2461 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2463 root.RemoveChild (f);
2466 XmlElement e = WriteElement(root, "TypeParameters");
2468 var nodes = e.SelectNodes ("TypeParameter").Cast<XmlElement> ().ToArray ();
2470 foreach (GenericParameter t in typeParams) {
2472 IList<TypeReference> constraints = t.Constraints;
2473 GenericParameterAttributes attrs = t.Attributes;
2479 var baseType = e.SelectSingleNode("BaseTypeName");
2480 // TODO: should this comparison take into account BaseTypeName?
2481 return x.GetAttribute("Name") == t.Name;
2483 x => {}, // no additional action required
2486 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2488 pe.SetAttribute("Name", t.Name);
2489 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""), t.DeclaringType);
2490 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2491 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2499 ce = root.OwnerDocument.CreateElement ("Constraints");
2501 pe.AppendChild (ce);
2502 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2503 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2504 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2505 AppendElementText (ce, "ParameterAttribute", "Covariant");
2506 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2507 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2508 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2509 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2510 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2511 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2512 foreach (TypeReference c in constraints) {
2513 TypeDefinition cd = c.Resolve ();
2514 AppendElementText (ce,
2515 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2516 GetDocTypeFullName (c));
2525 private void MakeParameters (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew)
2527 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2528 MakeParameters (root, mi, ((MethodDefinition)mi).Parameters, shouldDuplicateWithNew);
2529 else if (mi is MethodDefinition) {
2530 MethodDefinition mb = (MethodDefinition) mi;
2531 IList<ParameterDefinition> parameters = mb.Parameters;
2532 MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
2533 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2534 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2535 p.SetAttribute ("RefType", "this");
2538 else if (mi is PropertyDefinition) {
2539 IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
2540 if (parameters.Count > 0)
2541 MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
2545 else if (mi is FieldDefinition) return;
2546 else if (mi is EventDefinition) return;
2547 else throw new ArgumentException();
2550 internal static string GetDocParameterType (TypeReference type)
2552 return GetDocTypeFullName (type).Replace ("@", "&");
2555 private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes, bool shouldDuplicateWithNew=false)
2557 XmlElement e = WriteElement(root, "ReturnValue");
2558 var valueToUse = GetDocTypeFullName (type);
2560 AddXmlNode (e.SelectNodes("ReturnType").Cast<XmlElement> ().ToArray (),
2561 x => x.InnerText == valueToUse,
2562 x => x.InnerText = valueToUse,
2564 var newNode = WriteElementText(e, "ReturnType", valueToUse, forceNewElement: true);
2565 if (attributes != null)
2566 MakeAttributes(e, GetCustomAttributes (attributes, ""), type);
2573 private void MakeReturnValue (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew=false)
2575 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2577 else if (mi is MethodDefinition)
2578 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes, shouldDuplicateWithNew);
2579 else if (mi is PropertyDefinition)
2580 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null, shouldDuplicateWithNew);
2581 else if (mi is FieldDefinition)
2582 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null, shouldDuplicateWithNew);
2583 else if (mi is EventDefinition)
2584 MakeReturnValue (root, ((EventDefinition)mi).EventType, null, shouldDuplicateWithNew);
2586 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2589 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2591 MemberReference mi = info.Member;
2592 if (mi is TypeDefinition) return null;
2594 string sigs = memberFormatters [0].GetDeclaration (mi);
2595 if (sigs == null) return null; // not publicly visible
2597 // no documentation for property/event accessors. Is there a better way of doing this?
2598 if (mi.Name.StartsWith("get_")) return null;
2599 if (mi.Name.StartsWith("set_")) return null;
2600 if (mi.Name.StartsWith("add_")) return null;
2601 if (mi.Name.StartsWith("remove_")) return null;
2602 if (mi.Name.StartsWith("raise_")) return null;
2604 XmlElement me = doc.CreateElement("Member");
2605 me.SetAttribute("MemberName", GetMemberName (mi));
2609 if (exceptions.HasValue &&
2610 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2611 UpdateExceptions (info.Node, info.Member);
2613 if (since != null) {
2614 XmlNode docs = me.SelectSingleNode("Docs");
2615 docs.AppendChild (CreateSinceNode (doc));
2621 internal static string GetMemberName (MemberReference mi)
2623 MethodDefinition mb = mi as MethodDefinition;
2625 PropertyDefinition pi = mi as PropertyDefinition;
2628 return DocUtils.GetPropertyName (pi);
2630 StringBuilder sb = new StringBuilder (mi.Name.Length);
2631 if (!DocUtils.IsExplicitlyImplemented (mb))
2632 sb.Append (mi.Name);
2634 TypeReference iface;
2635 MethodReference ifaceMethod;
2636 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2637 sb.Append (GetDocTypeFullName (iface));
2639 sb.Append (ifaceMethod.Name);
2641 if (mb.IsGenericMethod ()) {
2642 IList<GenericParameter> typeParams = mb.GenericParameters;
2643 if (typeParams.Count > 0) {
2645 sb.Append (typeParams [0].Name);
2646 for (int i = 1; i < typeParams.Count; ++i)
2647 sb.Append (",").Append (typeParams [i].Name);
2651 return sb.ToString ();
2654 /// SIGNATURE GENERATION FUNCTIONS
2655 internal static bool IsPrivate (MemberReference mi)
2657 return memberFormatters [0].GetDeclaration (mi) == null;
2660 internal static string GetMemberType (MemberReference mi)
2662 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2663 return "Constructor";
2664 if (mi is MethodDefinition)
2666 if (mi is PropertyDefinition)
2668 if (mi is FieldDefinition)
2670 if (mi is EventDefinition)
2672 throw new ArgumentException();
2675 private static string GetDocTypeName (TypeReference type)
2677 return docTypeFormatter.GetName (type);
2680 internal static string GetDocTypeFullName (TypeReference type)
2682 return DocTypeFullMemberFormatter.Default.GetName (type);
2685 internal static string GetXPathForMember (DocumentationMember member)
2687 StringBuilder xpath = new StringBuilder ();
2688 xpath.Append ("//Members/Member[@MemberName=\"")
2689 .Append (member.MemberName)
2691 if (member.Parameters != null && member.Parameters.Count > 0) {
2692 xpath.Append ("/Parameters[count(Parameter) = ")
2693 .Append (member.Parameters.Count);
2694 for (int i = 0; i < member.Parameters.Count; ++i) {
2695 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2696 xpath.Append (member.Parameters [i]);
2697 xpath.Append ("\"");
2699 xpath.Append ("]/..");
2701 return xpath.ToString ();
2704 public static string GetXPathForMember (XPathNavigator member)
2706 StringBuilder xpath = new StringBuilder ();
2707 xpath.Append ("//Type[@FullName=\"")
2708 .Append (member.SelectSingleNode ("../../@FullName").Value)
2710 xpath.Append ("Members/Member[@MemberName=\"")
2711 .Append (member.SelectSingleNode ("@MemberName").Value)
2713 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2714 if (parameters.Count > 0) {
2715 xpath.Append ("/Parameters[count(Parameter) = ")
2716 .Append (parameters.Count);
2718 while (parameters.MoveNext ()) {
2720 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2721 xpath.Append (parameters.Current.Value);
2722 xpath.Append ("\"");
2724 xpath.Append ("]/..");
2726 return xpath.ToString ();
2729 public static string GetXPathForMember (MemberReference member)
2731 StringBuilder xpath = new StringBuilder ();
2732 xpath.Append ("//Type[@FullName=\"")
2733 .Append (member.DeclaringType.FullName)
2735 xpath.Append ("Members/Member[@MemberName=\"")
2736 .Append (GetMemberName (member))
2739 IList<ParameterDefinition> parameters = null;
2740 if (member is MethodDefinition)
2741 parameters = ((MethodDefinition) member).Parameters;
2742 else if (member is PropertyDefinition) {
2743 parameters = ((PropertyDefinition) member).Parameters;
2745 if (parameters != null && parameters.Count > 0) {
2746 xpath.Append ("/Parameters[count(Parameter) = ")
2747 .Append (parameters.Count);
2748 for (int i = 0; i < parameters.Count; ++i) {
2749 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2750 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2751 xpath.Append ("\"");
2753 xpath.Append ("]/..");
2755 return xpath.ToString ();
2759 static class CecilExtensions {
2760 public static string GetDeclaringType(this CustomAttribute attribute)
2762 var type = attribute.Constructor.DeclaringType;
2763 var typeName = type.FullName;
2765 string translatedType = NativeTypeManager.GetTranslatedName (type);
2766 return translatedType;
2769 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
2771 foreach (var c in type.Methods.Where (m => m.IsConstructor))
2772 yield return (MemberReference) c;
2773 foreach (var e in type.Events)
2774 yield return (MemberReference) e;
2775 foreach (var f in type.Fields)
2776 yield return (MemberReference) f;
2777 foreach (var m in type.Methods.Where (m => !m.IsConstructor))
2778 yield return (MemberReference) m;
2779 foreach (var t in type.NestedTypes)
2780 yield return (MemberReference) t;
2781 foreach (var p in type.Properties)
2782 yield return (MemberReference) p;
2785 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
2787 return GetMembers (type).Where (m => m.Name == member);
2790 public static MemberReference GetMember (this TypeDefinition type, string member)
2792 return GetMembers (type, member).EnsureZeroOrOne ();
2795 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2797 if (source.Count () > 1)
2798 throw new InvalidOperationException ("too many matches");
2799 return source.FirstOrDefault ();
2802 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2805 .Where (m => m.Name == method)
2806 .EnsureZeroOrOne ();
2809 public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
2811 TypeDefinition def = type as TypeDefinition;
2813 return new MemberReference [0];
2814 CustomAttribute defMemberAttr = def.CustomAttributes
2815 .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
2816 if (defMemberAttr == null)
2817 return new MemberReference [0];
2818 string name = (string) defMemberAttr.ConstructorArguments [0].Value;
2819 return def.Properties
2820 .Where (p => p.Name == name)
2821 .Select (p => (MemberReference) p);
2824 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2826 return assembly.Modules.SelectMany (md => md.GetAllTypes ());
2829 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2831 return GetTypes (assembly)
2832 .Where (td => td.FullName == type)
2833 .EnsureZeroOrOne ();
2836 public static bool IsGenericType (this TypeReference type)
2838 return type.GenericParameters.Count > 0;
2841 public static bool IsGenericMethod (this MethodReference method)
2843 return method.GenericParameters.Count > 0;
2846 public static MemberReference Resolve (this MemberReference member)
2848 FieldReference fr = member as FieldReference;
2850 return fr.Resolve ();
2851 MethodReference mr = member as MethodReference;
2853 return mr.Resolve ();
2854 TypeReference tr = member as TypeReference;
2856 return tr.Resolve ();
2857 PropertyReference pr = member as PropertyReference;
2860 EventReference er = member as EventReference;
2863 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2866 public static TypeReference GetUnderlyingType (this TypeDefinition type)
2870 return type.Fields.First (f => f.Name == "value__").FieldType;
2873 public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
2875 return self.Types.SelectMany (t => t.GetAllTypes ());
2878 static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
2882 if (!self.HasNestedTypes)
2885 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
2895 static class DocUtils {
2897 public static bool DoesNotHaveApiStyle(this XmlElement element, ApiStyle style) {
2898 string styleString = style.ToString ().ToLower ();
2899 string apistylevalue = element.GetAttribute ("apistyle");
2900 return apistylevalue != styleString || string.IsNullOrWhiteSpace(apistylevalue);
2902 public static bool HasApiStyle(this XmlElement element, ApiStyle style) {
2903 string styleString = style.ToString ().ToLower ();
2904 return element.GetAttribute ("apistyle") == styleString;
2906 public static void AddApiStyle(this XmlElement element, ApiStyle style) {
2907 string styleString = style.ToString ().ToLower ();
2908 var existingValue = element.GetAttribute ("apistyle");
2909 if (string.IsNullOrWhiteSpace (existingValue) || existingValue != styleString) {
2910 element.SetAttribute ("apistyle", styleString);
2914 public static bool IsExplicitlyImplemented (MethodDefinition method)
2916 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2919 public static string GetTypeDotMember (string name)
2921 int startType, startMethod;
2922 startType = startMethod = -1;
2923 for (int i = 0; i < name.Length; ++i) {
2924 if (name [i] == '.') {
2925 startType = startMethod;
2929 return name.Substring (startType+1);
2932 public static string GetMember (string name)
2934 int i = name.LastIndexOf ('.');
2937 return name.Substring (i+1);
2940 public static void GetInfoForExplicitlyImplementedMethod (
2941 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2945 if (method.Overrides.Count != 1)
2946 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2947 iface = method.Overrides [0].DeclaringType;
2948 ifaceMethod = method.Overrides [0];
2951 public static string GetPropertyName (PropertyDefinition pi)
2953 // Issue: (g)mcs-generated assemblies that explicitly implement
2954 // properties don't specify the full namespace, just the
2955 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2956 MethodDefinition method = pi.GetMethod;
2958 method = pi.SetMethod;
2959 if (!IsExplicitlyImplemented (method))
2962 // Need to determine appropriate namespace for this member.
2963 TypeReference iface;
2964 MethodReference ifaceMethod;
2965 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2966 return string.Join (".", new string[]{
2967 DocTypeFullMemberFormatter.Default.GetName (iface),
2968 GetMember (pi.Name)});
2971 public static string GetNamespace (TypeReference type)
2973 if (type.GetElementType ().IsNested)
2974 type = type.GetElementType ();
2975 while (type != null && type.IsNested)
2976 type = type.DeclaringType;
2978 return string.Empty;
2980 string typeNS = type.Namespace;
2982 // first, make sure this isn't a type reference to another assembly/module
2984 bool isInAssembly = MDocUpdater.IsInAssemblies(type.Module.Name);
2985 if (isInAssembly && !typeNS.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (type)) {
2986 typeNS = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typeNS);
2991 public static string PathCombine (string dir, string path)
2997 return Path.Combine (dir, path);
3000 public static bool IsExtensionMethod (MethodDefinition method)
3003 method.CustomAttributes
3004 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
3005 && method.DeclaringType.CustomAttributes
3006 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
3009 public static bool IsDelegate (TypeDefinition type)
3011 TypeReference baseRef = type.BaseType;
3012 if (baseRef == null)
3014 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
3015 baseRef.FullName == "System.MulticastDelegate";
3018 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
3020 List<TypeReference> decls = new List<TypeReference> ();
3022 while (type.DeclaringType != null) {
3023 decls.Add (type.DeclaringType);
3024 type = type.DeclaringType;
3030 public static int GetGenericArgumentCount (TypeReference type)
3032 GenericInstanceType inst = type as GenericInstanceType;
3034 ? inst.GenericArguments.Count
3035 : type.GenericParameters.Count;
3038 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
3040 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
3041 List<TypeReference> userInterfaces = new List<TypeReference> ();
3042 foreach (TypeReference iface in type.Interfaces) {
3043 TypeReference lookup = iface.Resolve () ?? iface;
3044 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
3045 userInterfaces.Add (iface);
3047 return userInterfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()));
3050 private static string GetQualifiedTypeName (TypeReference type)
3052 return "[" + type.Scope.Name + "]" + type.FullName;
3055 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
3057 HashSet<string> inheritedInterfaces = new HashSet<string> ();
3058 Action<TypeDefinition> a = null;
3060 if (t == null) return;
3061 foreach (TypeReference r in t.Interfaces) {
3062 inheritedInterfaces.Add (GetQualifiedTypeName (r));
3066 TypeReference baseRef = type.BaseType;
3067 while (baseRef != null) {
3068 TypeDefinition baseDef = baseRef.Resolve ();
3069 if (baseDef != null) {
3071 baseRef = baseDef.BaseType;
3076 foreach (TypeReference r in type.Interfaces)
3078 return inheritedInterfaces;
3082 class DocsNodeInfo {
3083 public DocsNodeInfo (XmlElement node)
3088 public DocsNodeInfo (XmlElement node, TypeDefinition type)
3094 public DocsNodeInfo (XmlElement node, MemberReference member)
3097 SetMemberInfo (member);
3100 void SetType (TypeDefinition type)
3103 throw new ArgumentNullException ("type");
3105 GenericParameters = new List<GenericParameter> (type.GenericParameters);
3106 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
3107 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
3108 for (int i = 0; i < declTypes.Count - 1; ++i) {
3109 int remove = System.Math.Min (maxGenArgs,
3110 DocUtils.GetGenericArgumentCount (declTypes [i]));
3111 maxGenArgs -= remove;
3112 while (remove-- > 0)
3113 GenericParameters.RemoveAt (0);
3115 if (DocUtils.IsDelegate (type)) {
3116 Parameters = type.GetMethod("Invoke").Parameters;
3117 ReturnType = type.GetMethod("Invoke").ReturnType;
3118 ReturnIsReturn = true;
3122 void SetMemberInfo (MemberReference member)
3125 throw new ArgumentNullException ("member");
3126 ReturnIsReturn = true;
3130 if (member is MethodReference ) {
3131 MethodReference mr = (MethodReference) member;
3132 Parameters = mr.Parameters;
3133 if (mr.IsGenericMethod ()) {
3134 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
3137 else if (member is PropertyDefinition) {
3138 Parameters = ((PropertyDefinition) member).Parameters;
3141 if (member is MethodDefinition) {
3142 ReturnType = ((MethodDefinition) member).ReturnType;
3143 } else if (member is PropertyDefinition) {
3144 ReturnType = ((PropertyDefinition) member).PropertyType;
3145 ReturnIsReturn = false;
3148 // no remarks section for enum members
3149 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
3153 public TypeReference ReturnType;
3154 public List<GenericParameter> GenericParameters;
3155 public IList<ParameterDefinition> Parameters;
3156 public bool ReturnIsReturn;
3157 public XmlElement Node;
3158 public bool AddRemarks = true;
3159 public MemberReference Member;
3160 public TypeDefinition Type;
3162 public override string ToString ()
3164 return string.Format ("{0} - {1} - {2}", Type, Member, Node == null ? "no xml" : "with xml");
3168 class DocumentationEnumerator {
3170 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3172 return GetDocumentationTypes (assembly, forTypes, null);
3175 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3177 foreach (TypeDefinition type in assembly.GetTypes()) {
3178 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
3180 if (seen != null && seen.Contains (type.FullName))
3183 foreach (TypeDefinition nested in type.NestedTypes)
3184 yield return nested;
3188 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3190 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
3191 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
3192 oldmember.RemoveAttribute ("__monodocer-seen__");
3195 MemberReference m = GetMember (type, new DocumentationMember (oldmember));
3197 yield return new DocsNodeInfo (oldmember);
3200 yield return new DocsNodeInfo (oldmember, m);
3205 protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
3207 string membertype = member.MemberType;
3209 string returntype = member.ReturnType;
3211 string docName = member.MemberName;
3213 string[] docTypeParams = GetTypeParameters (docName);
3215 // If we're using 'magic types', then we might get false positives ... in those cases, we keep searching
3216 MemberReference likelyCandidate = null;
3218 // Loop through all members in this type with the same name
3219 var reflectedMembers = GetReflectionMembers (type, docName).ToArray ();
3220 foreach (MemberReference mi in reflectedMembers) {
3221 bool matchedMagicType = false;
3222 if (mi is TypeDefinition) continue;
3223 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
3225 if (MDocUpdater.IsPrivate (mi))
3228 IList<ParameterDefinition> pis = null;
3229 string[] typeParams = null;
3230 if (mi is MethodDefinition) {
3231 MethodDefinition mb = (MethodDefinition) mi;
3232 pis = mb.Parameters;
3233 if (mb.IsGenericMethod ()) {
3234 IList<GenericParameter> args = mb.GenericParameters;
3235 typeParams = args.Select (p => p.Name).ToArray ();
3238 else if (mi is PropertyDefinition)
3239 pis = ((PropertyDefinition)mi).Parameters;
3241 // check type parameters
3242 int methodTcount = member.TypeParameters == null ? 0 : member.TypeParameters.Count;
3243 int reflectionTcount = typeParams == null ? 0 : typeParams.Length;
3244 if (methodTcount != reflectionTcount)
3247 // check member parameters
3248 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
3249 int pcount = pis == null ? 0 : pis.Count;
3250 if (mcount != pcount)
3253 MethodDefinition mDef = mi as MethodDefinition;
3254 if (mDef != null && !mDef.IsConstructor) {
3255 // Casting operators can overload based on return type.
3256 string rtype = GetReplacedString (
3257 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
3258 typeParams, docTypeParams);
3259 string originalRType = rtype;
3260 if (MDocUpdater.SwitchingToMagicTypes) {
3261 rtype = NativeTypeManager.ConvertFromNativeType (rtype);
3264 if ((returntype != rtype && originalRType == rtype) ||
3265 (MDocUpdater.SwitchingToMagicTypes && returntype != originalRType && returntype != rtype && originalRType != rtype)) {
3269 if (originalRType != rtype)
3270 matchedMagicType = true;
3276 for (int i = 0; i < pis.Count; i++) {
3277 string paramType = GetReplacedString (
3278 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
3279 typeParams, docTypeParams);
3281 // if magictypes, replace paramType to "classic value" ... so the comparison works
3282 string originalParamType = paramType;
3283 if (MDocUpdater.SwitchingToMagicTypes) {
3284 paramType = NativeTypeManager.ConvertFromNativeType (paramType);
3287 string xmlMemberType = member.Parameters [i];
3288 if ((!paramType.Equals(xmlMemberType) && paramType.Equals(originalParamType)) ||
3289 (MDocUpdater.SwitchingToMagicTypes && !originalParamType.Equals(xmlMemberType) && !paramType.Equals(xmlMemberType) && !paramType.Equals(originalParamType))) {
3291 // did not match ... if we're dropping the namespace, and the paramType has the dropped
3292 // namespace, we should see if it matches when added
3293 bool stillDoesntMatch = true;
3294 if (MDocUpdater.HasDroppedNamespace(type) && paramType.StartsWith (MDocUpdater.droppedNamespace)) {
3295 string withDroppedNs = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, xmlMemberType);
3297 stillDoesntMatch = withDroppedNs != paramType;
3300 if (stillDoesntMatch) {
3306 if (originalParamType != paramType)
3307 matchedMagicType = true;
3309 if (!good) continue;
3311 if (MDocUpdater.SwitchingToMagicTypes && likelyCandidate == null && matchedMagicType) {
3312 // 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
3313 likelyCandidate = mi;
3320 return likelyCandidate;
3323 static string[] GetTypeParameters (string docName)
3325 if (docName [docName.Length-1] != '>')
3327 StringList types = new StringList ();
3328 int endToken = docName.Length-2;
3329 int i = docName.Length-2;
3331 if (docName [i] == ',' || docName [i] == '<') {
3332 types.Add (docName.Substring (i + 1, endToken - i));
3335 if (docName [i] == '<')
3340 return types.ToArray ();
3343 protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
3345 // In case of dropping the namespace, we have to remove the dropped NS
3346 // so that docName will match what's in the assembly/type
3347 if (MDocUpdater.HasDroppedNamespace (type) && docName.StartsWith(MDocUpdater.droppedNamespace + ".")) {
3348 int droppedNsLength = MDocUpdater.droppedNamespace.Length;
3349 docName = docName.Substring (droppedNsLength + 1, docName.Length - droppedNsLength - 1);
3352 // need to worry about 4 forms of //@MemberName values:
3353 // 1. "Normal" (non-generic) member names: GetEnumerator
3355 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
3356 // - try as-is, and try type.member (due to "kludge" for property
3358 // 3. "Normal" Generic member names: Sort<T> (CSC)
3359 // - need to remove generic parameters --> "Sort"
3360 // 4. Explicitly-implemented interface members for generic interfaces:
3361 // -- System.Collections.Generic.IEnumerable<T>.Current
3362 // - Try as-is, and try type.member, *keeping* the generic parameters.
3363 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
3364 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
3365 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
3366 // this as (1) or (2).
3367 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
3369 foreach (MemberReference mi in type.GetMembers (docName))
3371 if (CountChars (docName, '.') > 0)
3372 // might be a property; try only type.member instead of
3373 // namespace.type.member.
3374 foreach (MemberReference mi in
3375 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
3382 int startLt, startType, startMethod;
3383 startLt = startType = startMethod = -1;
3384 for (int i = 0; i < docName.Length; ++i) {
3385 switch (docName [i]) {
3394 if (numLt == 0 && (i + 1) < docName.Length)
3395 // there's another character in docName, so this <...> sequence is
3396 // probably part of a generic type -- case 4.
3400 startType = startMethod;
3406 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
3408 foreach (MemberReference mi in type.GetMembers (refName))
3412 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
3415 // If we _still_ haven't found it, we've hit another generic naming issue:
3416 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
3417 // explicitly-implemented METHOD names (not properties), e.g.
3418 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
3419 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
3420 // which the XML docs will contain.
3422 // Alas, we can't derive the Mono name from docName, so we need to iterate
3423 // over all member names, convert them into CSC format, and compare... :-(
3426 foreach (MemberReference mi in type.GetMembers ()) {
3427 if (MDocUpdater.GetMemberName (mi) == docName)
3432 static string GetReplacedString (string typeName, string[] from, string[] to)
3436 for (int i = 0; i < from.Length; ++i)
3437 typeName = typeName.Replace (from [i], to [i]);
3441 private static int CountChars (string s, char c)
3444 for (int i = 0; i < s.Length; ++i) {
3452 class EcmaDocumentationEnumerator : DocumentationEnumerator {
3457 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
3460 this.ecmadocs = ecmaDocs;
3463 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3465 HashSet<string> seen = new HashSet<string> ();
3466 return GetDocumentationTypes (assembly, forTypes, seen)
3467 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
3470 new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3473 while (ecmadocs.Read ()) {
3474 switch (ecmadocs.Name) {
3476 if (typeDepth == -1)
3477 typeDepth = ecmadocs.Depth;
3478 if (ecmadocs.NodeType != XmlNodeType.Element)
3480 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
3482 string typename = ecmadocs.GetAttribute ("FullName");
3483 string typename2 = MDocUpdater.GetTypeFileName (typename);
3484 if (forTypes != null &&
3485 forTypes.BinarySearch (typename) < 0 &&
3486 typename != typename2 &&
3487 forTypes.BinarySearch (typename2) < 0)
3490 if ((t = assembly.GetType (typename)) == null &&
3491 (t = assembly.GetType (typename2)) == null)
3493 seen.Add (typename);
3494 if (typename != typename2)
3495 seen.Add (typename2);
3496 Console.WriteLine (" Import: {0}", t.FullName);
3497 if (ecmadocs.Name != "Docs") {
3498 int depth = ecmadocs.Depth;
3499 while (ecmadocs.Read ()) {
3500 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
3504 if (!ecmadocs.IsStartElement ("Docs"))
3505 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
3515 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3517 return GetMembers (basefile, type)
3518 .Concat (base.GetDocumentationMembers (basefile, type));
3521 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
3523 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
3526 if (ecmadocs.IsEmptyElement)
3529 int membersDepth = ecmadocs.Depth;
3531 while (go && ecmadocs.Read ()) {
3532 switch (ecmadocs.Name) {
3534 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
3536 DocumentationMember dm = new DocumentationMember (ecmadocs);
3538 string xp = MDocUpdater.GetXPathForMember (dm);
3539 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
3541 if (oldmember == null) {
3542 m = GetMember (type, dm);
3544 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3545 type.FullName, dm.MemberSignatures ["C#"]);
3546 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
3549 // oldmember lookup may have failed due to type parameter renames.
3551 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
3552 if (oldmember == null) {
3553 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
3554 oldmember = basefile.CreateElement ("Member");
3555 oldmember.SetAttribute ("MemberName", dm.MemberName);
3556 members.AppendChild (oldmember);
3557 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
3558 XmlElement ms = basefile.CreateElement ("MemberSignature");
3559 ms.SetAttribute ("Language", key);
3560 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
3561 oldmember.AppendChild (ms);
3563 oldmember.SetAttribute ("__monodocer-seen__", "true");
3564 Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
3569 m = GetMember (type, new DocumentationMember (oldmember));
3571 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3572 type.FullName, dm.MemberSignatures ["C#"]);
3575 oldmember.SetAttribute ("__monodocer-seen__", "true");
3577 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
3578 if (ecmadocs.Name != "Docs")
3579 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
3584 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
3593 abstract class DocumentationImporter {
3595 public abstract void ImportDocumentation (DocsNodeInfo info);
3598 class MsxdocDocumentationImporter : DocumentationImporter {
3600 XmlDocument slashdocs;
3602 public MsxdocDocumentationImporter (string file)
3604 var xml = File.ReadAllText (file);
3606 // Ensure Unix line endings
3607 xml = xml.Replace ("\r", "");
3609 slashdocs = new XmlDocument();
3610 slashdocs.LoadXml (xml);
3613 public override void ImportDocumentation (DocsNodeInfo info)
3615 XmlNode elem = GetDocs (info.Member ?? info.Type);
3620 XmlElement e = info.Node;
3622 if (elem.SelectSingleNode("summary") != null)
3623 MDocUpdater.ClearElement(e, "summary");
3624 if (elem.SelectSingleNode("remarks") != null)
3625 MDocUpdater.ClearElement(e, "remarks");
3626 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3627 MDocUpdater.ClearElement(e, "value");
3628 MDocUpdater.ClearElement(e, "returns");
3631 foreach (XmlNode child in elem.ChildNodes) {
3632 switch (child.Name) {
3635 XmlAttribute name = child.Attributes ["name"];
3638 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3640 p2.InnerXml = child.InnerXml;
3643 // Occasionally XML documentation will use <returns/> on
3644 // properties, so let's try to normalize things.
3647 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3648 v.InnerXml = child.InnerXml;
3654 case "permission": {
3655 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3658 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3660 a = e.OwnerDocument.CreateElement (child.Name);
3661 a.SetAttribute ("cref", cref.Value);
3664 a.InnerXml = child.InnerXml;
3668 XmlAttribute cref = child.Attributes ["cref"];
3671 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3673 a = e.OwnerDocument.CreateElement ("altmember");
3674 a.SetAttribute ("cref", cref.Value);
3681 if (child.NodeType == XmlNodeType.Element &&
3682 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3685 MDocUpdater.CopyNode (child, e);
3692 private XmlNode GetDocs (MemberReference member)
3694 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3695 if (slashdocsig != null)
3696 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3701 class EcmaDocumentationImporter : DocumentationImporter {
3705 public EcmaDocumentationImporter (XmlReader ecmaDocs)
3707 this.ecmadocs = ecmaDocs;
3710 public override void ImportDocumentation (DocsNodeInfo info)
3712 if (!ecmadocs.IsStartElement ("Docs")) {
3716 XmlElement e = info.Node;
3718 int depth = ecmadocs.Depth;
3719 ecmadocs.ReadStartElement ("Docs");
3720 while (ecmadocs.Read ()) {
3721 if (ecmadocs.Name == "Docs") {
3722 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3725 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3727 if (!ecmadocs.IsStartElement ())
3729 switch (ecmadocs.Name) {
3732 string name = ecmadocs.GetAttribute ("name");
3735 XmlNode doc = e.SelectSingleNode (
3736 ecmadocs.Name + "[@name='" + name + "']");
3737 string value = ecmadocs.ReadInnerXml ();
3739 doc.InnerXml = value.Replace ("\r", "");
3746 string name = ecmadocs.Name;
3747 string cref = ecmadocs.GetAttribute ("cref");
3750 XmlNode doc = e.SelectSingleNode (
3751 ecmadocs.Name + "[@cref='" + cref + "']");
3752 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3754 doc.InnerXml = value;
3756 XmlElement n = e.OwnerDocument.CreateElement (name);
3757 n.SetAttribute ("cref", cref);
3764 string name = ecmadocs.Name;
3765 string xpath = ecmadocs.Name;
3766 StringList attributes = new StringList (ecmadocs.AttributeCount);
3767 if (ecmadocs.MoveToFirstAttribute ()) {
3769 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3770 } while (ecmadocs.MoveToNextAttribute ());
3771 ecmadocs.MoveToContent ();
3773 if (attributes.Count > 0) {
3774 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3776 XmlNode doc = e.SelectSingleNode (xpath);
3777 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3779 doc.InnerXml = value;
3782 XmlElement n = e.OwnerDocument.CreateElement (name);
3784 foreach (string a in attributes) {
3785 int eq = a.IndexOf ('=');
3786 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3797 class DocumentationMember {
3798 public StringToStringMap MemberSignatures = new StringToStringMap ();
3799 public string ReturnType;
3800 public StringList Parameters;
3801 public StringList TypeParameters;
3802 public string MemberName;
3803 public string MemberType;
3805 public DocumentationMember (XmlReader reader)
3807 MemberName = reader.GetAttribute ("MemberName");
3808 int depth = reader.Depth;
3810 StringList p = new StringList ();
3811 StringList tp = new StringList ();
3813 if (reader.NodeType != XmlNodeType.Element)
3816 bool shouldUse = true;
3818 string apistyle = reader.GetAttribute ("apistyle");
3819 shouldUse = string.IsNullOrWhiteSpace(apistyle) || apistyle == "classic"; // only use this tag if it's an 'classic' style node
3821 catch (Exception ex) {}
3822 switch (reader.Name) {
3823 case "MemberSignature":
3825 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3829 MemberType = reader.ReadElementString ();
3832 if (reader.Depth == depth + 2 && shouldUse)
3833 ReturnType = reader.ReadElementString ();
3836 if (reader.Depth == depth + 2 && shouldUse)
3837 p.Add (reader.GetAttribute ("Type"));
3839 case "TypeParameter":
3840 if (reader.Depth == depth + 2 && shouldUse)
3841 tp.Add (reader.GetAttribute ("Name"));
3844 if (reader.Depth == depth + 1)
3848 } while (go && reader.Read () && reader.Depth >= depth);
3853 TypeParameters = tp;
3855 DiscernTypeParameters ();
3859 public DocumentationMember (XmlNode node)
3861 MemberName = node.Attributes ["MemberName"].Value;
3862 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3863 XmlAttribute l = n.Attributes ["Language"];
3864 XmlAttribute v = n.Attributes ["Value"];
3865 XmlAttribute apistyle = n.Attributes ["apistyle"];
3866 bool shouldUse = apistyle == null || apistyle.Value == "classic";
3867 if (l != null && v != null && shouldUse)
3868 MemberSignatures [l.Value] = v.Value;
3870 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3871 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType[not(@apistyle) or @apistyle='classic']");
3873 ReturnType = rt.InnerText;
3874 XmlNodeList p = node.SelectNodes ("Parameters/Parameter[not(@apistyle) or @apistyle='classic']");
3876 Parameters = new StringList (p.Count);
3877 for (int i = 0; i < p.Count; ++i)
3878 Parameters.Add (p [i].Attributes ["Type"].Value);
3880 XmlNodeList tp = node.SelectNodes ("TypeParameters/TypeParameter[not(@apistyle) or @apistyle='classic']");
3882 TypeParameters = new StringList (tp.Count);
3883 for (int i = 0; i < tp.Count; ++i)
3884 TypeParameters.Add (tp [i].Attributes ["Name"].Value);
3887 DiscernTypeParameters ();
3891 void DiscernTypeParameters ()
3893 // see if we can discern the param list from the name
3894 if (MemberName.Contains ("<") && MemberName.EndsWith (">")) {
3895 var starti = MemberName.IndexOf ("<") + 1;
3896 var endi = MemberName.LastIndexOf (">");
3897 var paramlist = MemberName.Substring (starti, endi - starti);
3898 var tparams = paramlist.Split (new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
3899 TypeParameters = new StringList (tparams);
3904 public class DynamicParserContext {
3905 public ReadOnlyCollection<bool> TransformFlags;
3906 public int TransformIndex;
3908 public DynamicParserContext (ICustomAttributeProvider provider)
3911 if (provider.HasCustomAttributes &&
3912 (da = (provider.CustomAttributes.Cast<CustomAttribute>()
3913 .SingleOrDefault (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))) != null) {
3914 CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
3915 ? new CustomAttributeArgument [0]
3916 : (CustomAttributeArgument[]) da.ConstructorArguments [0].Value;
3918 TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool) t.Value).ToArray());
3923 public enum MemberFormatterState {
3925 WithinGenericTypeParameters,
3928 public abstract class MemberFormatter {
3930 public virtual string Language {
3934 public string GetName (MemberReference member)
3936 return GetName (member, null);
3939 public virtual string GetName (MemberReference member, DynamicParserContext context)
3941 TypeReference type = member as TypeReference;
3943 return GetTypeName (type, context);
3944 MethodReference method = member as MethodReference;
3945 if (method != null && method.Name == ".ctor") // method.IsConstructor
3946 return GetConstructorName (method);
3948 return GetMethodName (method);
3949 PropertyReference prop = member as PropertyReference;
3951 return GetPropertyName (prop);
3952 FieldReference field = member as FieldReference;
3954 return GetFieldName (field);
3955 EventReference e = member as EventReference;
3957 return GetEventName (e);
3958 throw new NotSupportedException ("Can't handle: " +
3959 (member == null ? "null" : member.GetType().ToString()));
3962 protected virtual string GetTypeName (TypeReference type)
3964 return GetTypeName (type, null);
3967 protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
3970 throw new ArgumentNullException ("type");
3971 return _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
3974 protected virtual char[] ArrayDelimeters {
3975 get {return new char[]{'[', ']'};}
3978 protected virtual MemberFormatterState MemberFormatterState { get; set; }
3980 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3982 if (type is ArrayType) {
3983 TypeSpecification spec = type as TypeSpecification;
3984 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
3985 return AppendArrayModifiers (buf, (ArrayType) type);
3987 if (type is ByReferenceType) {
3988 return AppendRefTypeName (buf, type, context);
3990 if (type is PointerType) {
3991 return AppendPointerTypeName (buf, type, context);
3993 if (type is GenericParameter) {
3994 return AppendTypeName (buf, type, context);
3996 AppendNamespace (buf, type);
3997 GenericInstanceType genInst = type as GenericInstanceType;
3998 if (type.GenericParameters.Count == 0 &&
3999 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
4000 return AppendFullTypeName (buf, type, context);
4002 return AppendGenericType (buf, type, context);
4005 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4007 string ns = DocUtils.GetNamespace (type);
4008 if (ns != null && ns.Length > 0)
4009 buf.Append (ns).Append ('.');
4013 protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4015 if (type.DeclaringType != null)
4016 AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
4017 return AppendTypeName (buf, type, context);
4020 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4022 if (context != null)
4023 context.TransformIndex++;
4024 return AppendTypeName (buf, type.Name);
4027 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
4029 int n = typename.IndexOf ("`");
4031 return buf.Append (typename.Substring (0, n));
4032 return buf.Append (typename);
4035 protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
4037 buf.Append (ArrayDelimeters [0]);
4038 int rank = array.Rank;
4040 buf.Append (new string (',', rank-1));
4041 return buf.Append (ArrayDelimeters [1]);
4044 protected virtual string RefTypeModifier {
4048 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4050 TypeSpecification spec = type as TypeSpecification;
4051 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4052 .Append (RefTypeModifier);
4055 protected virtual string PointerModifier {
4059 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4061 TypeSpecification spec = type as TypeSpecification;
4062 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4063 .Append (PointerModifier);
4066 protected virtual char[] GenericTypeContainer {
4067 get {return new char[]{'<', '>'};}
4070 protected virtual char NestedTypeSeparator {
4074 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4076 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4077 type is GenericInstanceType ? type.GetElementType () : type);
4078 List<TypeReference> genArgs = GetGenericArguments (type);
4081 bool insertNested = false;
4082 foreach (var decl in decls) {
4083 TypeReference declDef = decl.Resolve () ?? decl;
4085 buf.Append (NestedTypeSeparator);
4087 insertNested = true;
4088 AppendTypeName (buf, declDef, context);
4089 int ac = DocUtils.GetGenericArgumentCount (declDef);
4093 buf.Append (GenericTypeContainer [0]);
4094 var origState = MemberFormatterState;
4095 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4096 _AppendTypeName (buf, genArgs [argIdx++], context);
4097 for (int i = 1; i < c; ++i) {
4098 _AppendTypeName (buf.Append (","), genArgs [argIdx++], context);
4100 MemberFormatterState = origState;
4101 buf.Append (GenericTypeContainer [1]);
4107 protected List<TypeReference> GetGenericArguments (TypeReference type)
4109 var args = new List<TypeReference> ();
4110 GenericInstanceType inst = type as GenericInstanceType;
4112 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
4114 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
4118 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4123 protected virtual string GetConstructorName (MethodReference constructor)
4125 return constructor.Name;
4128 protected virtual string GetMethodName (MethodReference method)
4133 protected virtual string GetPropertyName (PropertyReference property)
4135 return property.Name;
4138 protected virtual string GetFieldName (FieldReference field)
4143 protected virtual string GetEventName (EventReference e)
4148 public virtual string GetDeclaration (MemberReference member)
4151 throw new ArgumentNullException ("member");
4152 TypeDefinition type = member as TypeDefinition;
4154 return GetTypeDeclaration (type);
4155 MethodDefinition method = member as MethodDefinition;
4156 if (method != null && method.IsConstructor)
4157 return GetConstructorDeclaration (method);
4159 return GetMethodDeclaration (method);
4160 PropertyDefinition prop = member as PropertyDefinition;
4162 return GetPropertyDeclaration (prop);
4163 FieldDefinition field = member as FieldDefinition;
4165 return GetFieldDeclaration (field);
4166 EventDefinition e = member as EventDefinition;
4168 return GetEventDeclaration (e);
4169 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
4172 protected virtual string GetTypeDeclaration (TypeDefinition type)
4175 throw new ArgumentNullException ("type");
4176 StringBuilder buf = new StringBuilder (type.Name.Length);
4177 _AppendTypeName (buf, type, null);
4178 AppendGenericTypeConstraints (buf, type);
4179 return buf.ToString ();
4182 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
4184 return GetConstructorName (constructor);
4187 protected virtual string GetMethodDeclaration (MethodDefinition method)
4189 if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
4190 ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
4193 // Special signature for destructors.
4194 if (method.Name == "Finalize" && method.Parameters.Count == 0)
4195 return GetFinalizerName (method);
4197 StringBuilder buf = new StringBuilder ();
4199 AppendVisibility (buf, method);
4200 if (buf.Length == 0 &&
4201 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
4204 AppendModifiers (buf, method);
4206 if (buf.Length != 0)
4208 buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
4210 AppendMethodName (buf, method);
4211 AppendGenericMethod (buf, method).Append (" ");
4212 AppendParameters (buf, method, method.Parameters);
4213 AppendGenericMethodConstraints (buf, method);
4214 return buf.ToString ();
4217 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4219 return buf.Append (method.Name);
4222 protected virtual string GetFinalizerName (MethodDefinition method)
4227 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4232 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4237 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4242 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4247 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4252 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
4254 return GetPropertyName (property);
4257 protected virtual string GetFieldDeclaration (FieldDefinition field)
4259 return GetFieldName (field);
4262 protected virtual string GetEventDeclaration (EventDefinition e)
4264 return GetEventName (e);
4268 class ILFullMemberFormatter : MemberFormatter {
4270 public override string Language {
4271 get {return "ILAsm";}
4274 protected override char NestedTypeSeparator {
4280 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4282 if (GetBuiltinType (type.FullName) != null)
4284 string ns = DocUtils.GetNamespace (type);
4285 if (ns != null && ns.Length > 0) {
4286 if (type.IsValueType)
4287 buf.Append ("valuetype ");
4289 buf.Append ("class ");
4290 buf.Append (ns).Append ('.');
4295 protected static string GetBuiltinType (string t)
4298 case "System.Byte": return "unsigned int8";
4299 case "System.SByte": return "int8";
4300 case "System.Int16": return "int16";
4301 case "System.Int32": return "int32";
4302 case "System.Int64": return "int64";
4303 case "System.IntPtr": return "native int";
4305 case "System.UInt16": return "unsigned int16";
4306 case "System.UInt32": return "unsigned int32";
4307 case "System.UInt64": return "unsigned int64";
4308 case "System.UIntPtr": return "native unsigned int";
4310 case "System.Single": return "float32";
4311 case "System.Double": return "float64";
4312 case "System.Boolean": return "bool";
4313 case "System.Char": return "char";
4314 case "System.Void": return "void";
4315 case "System.String": return "string";
4316 case "System.Object": return "object";
4321 protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
4323 return buf.Append (typename);
4326 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4328 if (type is GenericParameter) {
4329 AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
4333 string s = GetBuiltinType (type.FullName);
4335 return buf.Append (s);
4337 return base.AppendTypeName (buf, type, context);
4340 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
4342 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
4343 return buf.Append (type.Owner is TypeReference ? "!" : "!!");
4345 GenericParameterAttributes attrs = type.Attributes;
4346 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
4347 buf.Append ("class ");
4348 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
4349 buf.Append ("struct ");
4350 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
4351 buf.Append (".ctor ");
4352 IList<TypeReference> constraints = type.Constraints;
4353 MemberFormatterState = 0;
4354 if (constraints.Count > 0) {
4355 var full = new ILFullMemberFormatter ();
4356 buf.Append ("(").Append (full.GetName (constraints [0]));
4357 for (int i = 1; i < constraints.Count; ++i) {
4358 buf.Append (", ").Append (full.GetName (constraints [i]));
4362 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4364 if ((attrs & GenericParameterAttributes.Covariant) != 0)
4366 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
4371 protected override string GetTypeDeclaration (TypeDefinition type)
4373 string visibility = GetTypeVisibility (type.Attributes);
4374 if (visibility == null)
4377 StringBuilder buf = new StringBuilder ();
4379 buf.Append (".class ");
4381 buf.Append ("nested ");
4382 buf.Append (visibility).Append (" ");
4383 if (type.IsInterface)
4384 buf.Append ("interface ");
4385 if (type.IsSequentialLayout)
4386 buf.Append ("sequential ");
4387 if (type.IsAutoLayout)
4388 buf.Append ("auto ");
4389 if (type.IsAnsiClass)
4390 buf.Append ("ansi ");
4391 if (type.IsAbstract)
4392 buf.Append ("abstract ");
4393 if (type.IsSerializable)
4394 buf.Append ("serializable ");
4396 buf.Append ("sealed ");
4397 if (type.IsBeforeFieldInit)
4398 buf.Append ("beforefieldinit ");
4399 var state = MemberFormatterState;
4400 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4401 buf.Append (GetName (type));
4402 MemberFormatterState = state;
4403 var full = new ILFullMemberFormatter ();
4404 if (type.BaseType != null) {
4405 buf.Append (" extends ");
4406 if (type.BaseType.FullName == "System.Object")
4407 buf.Append ("System.Object");
4409 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
4412 foreach (var name in type.Interfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()))
4413 .Select (i => full.GetName (i))
4414 .OrderBy (n => n)) {
4416 buf.Append (" implements ");
4425 return buf.ToString ();
4428 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4430 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4431 type is GenericInstanceType ? type.GetElementType () : type);
4433 foreach (var decl in decls) {
4434 TypeReference declDef = decl.Resolve () ?? decl;
4436 buf.Append (NestedTypeSeparator);
4439 AppendTypeName (buf, declDef, context);
4443 foreach (TypeReference arg in GetGenericArguments (type)) {
4447 _AppendTypeName (buf, arg, context);
4453 static string GetTypeVisibility (TypeAttributes ta)
4455 switch (ta & TypeAttributes.VisibilityMask) {
4456 case TypeAttributes.Public:
4457 case TypeAttributes.NestedPublic:
4460 case TypeAttributes.NestedFamily:
4461 case TypeAttributes.NestedFamORAssem:
4469 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4471 return GetMethodDeclaration (constructor);
4474 protected override string GetMethodDeclaration (MethodDefinition method)
4476 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
4479 var buf = new StringBuilder ();
4480 buf.Append (".method ");
4481 AppendVisibility (buf, method);
4482 if (method.IsStatic)
4483 buf.Append ("static ");
4484 if (method.IsHideBySig)
4485 buf.Append ("hidebysig ");
4486 if (method.IsPInvokeImpl) {
4487 var info = method.PInvokeInfo;
4488 buf.Append ("pinvokeimpl (\"")
4489 .Append (info.Module.Name)
4490 .Append ("\" as \"")
4491 .Append (info.EntryPoint)
4493 if (info.IsCharSetAuto)
4494 buf.Append (" auto");
4495 if (info.IsCharSetUnicode)
4496 buf.Append (" unicode");
4497 if (info.IsCharSetAnsi)
4498 buf.Append (" ansi");
4499 if (info.IsCallConvCdecl)
4500 buf.Append (" cdecl");
4501 if (info.IsCallConvStdCall)
4502 buf.Append (" stdcall");
4503 if (info.IsCallConvWinapi)
4504 buf.Append (" winapi");
4505 if (info.IsCallConvThiscall)
4506 buf.Append (" thiscall");
4507 if (info.SupportsLastError)
4508 buf.Append (" lasterr");
4511 if (method.IsSpecialName)
4512 buf.Append ("specialname ");
4513 if (method.IsRuntimeSpecialName)
4514 buf.Append ("rtspecialname ");
4515 if (method.IsNewSlot)
4516 buf.Append ("newslot ");
4517 if (method.IsVirtual)
4518 buf.Append ("virtual ");
4519 if (!method.IsStatic)
4520 buf.Append ("instance ");
4521 _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
4523 .Append (method.Name);
4524 if (method.IsGenericMethod ()) {
4525 var state = MemberFormatterState;
4526 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4527 IList<GenericParameter> args = method.GenericParameters;
4528 if (args.Count > 0) {
4530 _AppendTypeName (buf, args [0], null);
4531 for (int i = 1; i < args.Count; ++i)
4532 _AppendTypeName (buf.Append (", "), args [i], null);
4535 MemberFormatterState = state;
4540 for (int i = 0; i < method.Parameters.Count; ++i) {
4544 _AppendTypeName (buf, method.Parameters [i].ParameterType, new DynamicParserContext (method.Parameters [i]));
4546 buf.Append (method.Parameters [i].Name);
4550 buf.Append (" cil");
4551 if (method.IsRuntime)
4552 buf.Append (" runtime");
4553 if (method.IsManaged)
4554 buf.Append (" managed");
4556 return buf.ToString ();
4559 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4561 if (DocUtils.IsExplicitlyImplemented (method)) {
4562 TypeReference iface;
4563 MethodReference ifaceMethod;
4564 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4565 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4567 .Append (ifaceMethod.Name);
4569 return base.AppendMethodName (buf, method);
4572 protected override string RefTypeModifier {
4576 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4578 if (method.IsPublic)
4579 return buf.Append ("public ");
4580 if (method.IsFamilyAndAssembly)
4581 return buf.Append ("familyandassembly");
4582 if (method.IsFamilyOrAssembly)
4583 return buf.Append ("familyorassembly");
4584 if (method.IsFamily)
4585 return buf.Append ("family");
4589 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4591 string modifiers = String.Empty;
4592 if (method.IsStatic) modifiers += " static";
4593 if (method.IsVirtual && !method.IsAbstract) {
4594 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4595 else modifiers += " override";
4597 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4598 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4599 if (method.IsFinal) modifiers += " sealed";
4600 if (modifiers == " virtual sealed") modifiers = "";
4602 return buf.Append (modifiers);
4605 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4607 if (method.IsGenericMethod ()) {
4608 IList<GenericParameter> args = method.GenericParameters;
4609 if (args.Count > 0) {
4611 buf.Append (args [0].Name);
4612 for (int i = 1; i < args.Count; ++i)
4613 buf.Append (",").Append (args [i].Name);
4620 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4622 return AppendParameters (buf, method, parameters, '(', ')');
4625 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4629 if (parameters.Count > 0) {
4630 if (DocUtils.IsExtensionMethod (method))
4631 buf.Append ("this ");
4632 AppendParameter (buf, parameters [0]);
4633 for (int i = 1; i < parameters.Count; ++i) {
4635 AppendParameter (buf, parameters [i]);
4639 return buf.Append (end);
4642 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4644 if (parameter.ParameterType is ByReferenceType) {
4645 if (parameter.IsOut)
4646 buf.Append ("out ");
4648 buf.Append ("ref ");
4650 buf.Append (GetName (parameter.ParameterType)).Append (" ");
4651 return buf.Append (parameter.Name);
4654 protected override string GetPropertyDeclaration (PropertyDefinition property)
4656 MethodDefinition gm = null, sm = null;
4658 string get_visible = null;
4659 if ((gm = property.GetMethod) != null &&
4660 (DocUtils.IsExplicitlyImplemented (gm) ||
4661 (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
4662 get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
4663 string set_visible = null;
4664 if ((sm = property.SetMethod) != null &&
4665 (DocUtils.IsExplicitlyImplemented (sm) ||
4666 (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
4667 set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
4669 if ((set_visible == null) && (get_visible == null))
4672 StringBuilder buf = new StringBuilder ()
4673 .Append (".property ");
4674 if (!(gm ?? sm).IsStatic)
4675 buf.Append ("instance ");
4676 _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
4677 buf.Append (' ').Append (property.Name);
4678 if (!property.HasParameters || property.Parameters.Count == 0)
4679 return buf.ToString ();
4683 foreach (ParameterDefinition p in property.Parameters) {
4687 _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
4691 return buf.ToString ();
4694 protected override string GetFieldDeclaration (FieldDefinition field)
4696 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4697 if (declType.IsEnum && field.Name == "value__")
4698 return null; // This member of enums aren't documented.
4700 StringBuilder buf = new StringBuilder ();
4701 AppendFieldVisibility (buf, field);
4702 if (buf.Length == 0)
4705 buf.Insert (0, ".field ");
4708 buf.Append ("static ");
4709 if (field.IsInitOnly)
4710 buf.Append ("initonly ");
4711 if (field.IsLiteral)
4712 buf.Append ("literal ");
4713 _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
4714 buf.Append (' ').Append (field.Name);
4715 AppendFieldValue (buf, field);
4717 return buf.ToString ();
4720 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4723 return buf.Append ("public ");
4724 if (field.IsFamilyAndAssembly)
4725 return buf.Append ("familyandassembly ");
4726 if (field.IsFamilyOrAssembly)
4727 return buf.Append ("familyorassembly ");
4729 return buf.Append ("family ");
4733 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4735 // enums have a value__ field, which we ignore
4736 if (field.DeclaringType.IsGenericType ())
4738 if (field.HasConstant && field.IsLiteral) {
4741 val = field.Constant;
4746 buf.Append (" = ").Append ("null");
4747 else if (val is Enum)
4749 .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4751 .Append (val.ToString ())
4753 else if (val is IFormattable) {
4754 string value = ((IFormattable)val).ToString();
4757 buf.Append ("\"" + value + "\"");
4759 buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4768 protected override string GetEventDeclaration (EventDefinition e)
4770 StringBuilder buf = new StringBuilder ();
4771 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4776 buf.Append (".event ")
4777 .Append (GetName (e.EventType))
4781 return buf.ToString ();
4785 class ILMemberFormatter : ILFullMemberFormatter {
4786 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4792 class ILNativeTypeMemberFormatter : ILFullMemberFormatter {
4793 protected static string _GetBuiltinType (string t)
4795 //string moddedType = base.GetBuiltinType (t);
4797 //return moddedType;
4801 class CSharpNativeTypeMemberFormatter : CSharpFullMemberFormatter {
4802 protected override string GetCSharpType (string t) {
4803 string moddedType = base.GetCSharpType (t);
4805 switch (moddedType) {
4806 case "int": return "nint";
4811 case "System.Drawing.SizeF":
4812 return "CoreGraphics.CGSize";
4813 case "System.Drawing.PointF":
4814 return "CoreGraphics.CGPoint";
4815 case "System.Drawing.RectangleF":
4816 return "CoreGraphics.CGPoint";
4822 class CSharpFullMemberFormatter : MemberFormatter {
4824 public override string Language {
4828 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4831 string ns = DocUtils.GetNamespace (type);
4832 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
4833 buf.Append (ns).Append ('.');
4837 protected virtual string GetCSharpType (string t)
4840 case "System.Byte": return "byte";
4841 case "System.SByte": return "sbyte";
4842 case "System.Int16": return "short";
4843 case "System.Int32": return "int";
4844 case "System.Int64": return "long";
4846 case "System.UInt16": return "ushort";
4847 case "System.UInt32": return "uint";
4848 case "System.UInt64": return "ulong";
4850 case "System.Single": return "float";
4851 case "System.Double": return "double";
4852 case "System.Decimal": return "decimal";
4853 case "System.Boolean": return "bool";
4854 case "System.Char": return "char";
4855 case "System.Void": return "void";
4856 case "System.String": return "string";
4857 case "System.Object": return "object";
4862 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4864 if (context != null && context.TransformFlags != null &&
4865 (context.TransformFlags.Count == 0 || context.TransformFlags [context.TransformIndex])) {
4866 context.TransformIndex++;
4867 return buf.Append ("dynamic");
4870 if (type is GenericParameter)
4871 return AppendGenericParameterConstraints (buf, (GenericParameter) type, context).Append (type.Name);
4872 string t = type.FullName;
4873 if (!t.StartsWith ("System.")) {
4874 return base.AppendTypeName (buf, type, context);
4877 string s = GetCSharpType (t);
4879 if (context != null)
4880 context.TransformIndex++;
4881 return buf.Append (s);
4884 return base.AppendTypeName (buf, type, context);
4887 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
4889 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
4891 GenericParameterAttributes attrs = type.Attributes;
4892 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
4893 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
4897 buf.Append ("out ");
4901 protected override string GetTypeDeclaration (TypeDefinition type)
4903 string visibility = GetTypeVisibility (type.Attributes);
4904 if (visibility == null)
4907 StringBuilder buf = new StringBuilder ();
4909 buf.Append (visibility);
4912 MemberFormatter full = new CSharpFullMemberFormatter ();
4914 if (DocUtils.IsDelegate (type)) {
4915 buf.Append("delegate ");
4916 MethodDefinition invoke = type.GetMethod ("Invoke");
4917 buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
4918 buf.Append (GetName (type));
4919 AppendParameters (buf, invoke, invoke.Parameters);
4920 AppendGenericTypeConstraints (buf, type);
4923 return buf.ToString();
4926 if (type.IsAbstract && !type.IsInterface)
4927 buf.Append("abstract ");
4928 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
4929 buf.Append("sealed ");
4930 buf.Replace ("abstract sealed", "static");
4932 buf.Append (GetTypeKind (type));
4934 buf.Append (GetCSharpType (type.FullName) == null
4939 TypeReference basetype = type.BaseType;
4940 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
4943 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
4944 .Select (iface => full.GetName (iface))
4948 if (basetype != null || interface_names.Count > 0)
4951 if (basetype != null) {
4952 buf.Append (full.GetName (basetype));
4953 if (interface_names.Count > 0)
4957 for (int i = 0; i < interface_names.Count; i++){
4960 buf.Append (interface_names [i]);
4962 AppendGenericTypeConstraints (buf, type);
4965 return buf.ToString ();
4968 static string GetTypeKind (TypeDefinition t)
4974 if (t.IsClass || t.FullName == "System.Enum")
4978 throw new ArgumentException(t.FullName);
4981 static string GetTypeVisibility (TypeAttributes ta)
4983 switch (ta & TypeAttributes.VisibilityMask) {
4984 case TypeAttributes.Public:
4985 case TypeAttributes.NestedPublic:
4988 case TypeAttributes.NestedFamily:
4989 case TypeAttributes.NestedFamORAssem:
4997 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4999 if (type.GenericParameters.Count == 0)
5001 return AppendConstraints (buf, type.GenericParameters);
5004 private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
5006 foreach (GenericParameter genArg in genArgs) {
5007 GenericParameterAttributes attrs = genArg.Attributes;
5008 IList<TypeReference> constraints = genArg.Constraints;
5009 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
5012 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
5013 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
5014 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
5017 if (!isref && !isvt && !isnew && constraints.Count == 0)
5019 buf.Append (" where ").Append (genArg.Name).Append (" : ");
5021 buf.Append ("class");
5025 buf.Append ("struct");
5028 if (constraints.Count > 0 && !isvt) {
5031 buf.Append (GetTypeName (constraints [0]));
5032 for (int i = 1; i < constraints.Count; ++i)
5033 buf.Append (", ").Append (GetTypeName (constraints [i]));
5035 if (isnew && !isvt) {
5038 buf.Append ("new()");
5044 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5046 StringBuilder buf = new StringBuilder ();
5047 AppendVisibility (buf, constructor);
5048 if (buf.Length == 0)
5052 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
5053 AppendParameters (buf, constructor, constructor.Parameters);
5056 return buf.ToString ();
5059 protected override string GetMethodDeclaration (MethodDefinition method)
5061 string decl = base.GetMethodDeclaration (method);
5067 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
5069 if (DocUtils.IsExplicitlyImplemented (method)) {
5070 TypeReference iface;
5071 MethodReference ifaceMethod;
5072 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5073 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
5075 .Append (ifaceMethod.Name);
5077 return base.AppendMethodName (buf, method);
5080 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
5082 if (method.GenericParameters.Count == 0)
5084 return AppendConstraints (buf, method.GenericParameters);
5087 protected override string RefTypeModifier {
5091 protected override string GetFinalizerName (MethodDefinition method)
5093 return "~" + method.DeclaringType.Name + " ()";
5096 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
5100 if (method.IsPublic)
5101 return buf.Append ("public");
5102 if (method.IsFamily || method.IsFamilyOrAssembly)
5103 return buf.Append ("protected");
5107 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
5109 string modifiers = String.Empty;
5110 if (method.IsStatic) modifiers += " static";
5111 if (method.IsVirtual && !method.IsAbstract) {
5112 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
5113 else modifiers += " override";
5115 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
5116 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
5117 if (method.IsFinal) modifiers += " sealed";
5118 if (modifiers == " virtual sealed") modifiers = "";
5120 return buf.Append (modifiers);
5123 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
5125 if (method.IsGenericMethod ()) {
5126 IList<GenericParameter> args = method.GenericParameters;
5127 if (args.Count > 0) {
5129 buf.Append (args [0].Name);
5130 for (int i = 1; i < args.Count; ++i)
5131 buf.Append (",").Append (args [i].Name);
5138 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
5140 return AppendParameters (buf, method, parameters, '(', ')');
5143 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
5147 if (parameters.Count > 0) {
5148 if (DocUtils.IsExtensionMethod (method))
5149 buf.Append ("this ");
5150 AppendParameter (buf, parameters [0]);
5151 for (int i = 1; i < parameters.Count; ++i) {
5153 AppendParameter (buf, parameters [i]);
5157 return buf.Append (end);
5160 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
5162 if (parameter.ParameterType is ByReferenceType) {
5163 if (parameter.IsOut)
5164 buf.Append ("out ");
5166 buf.Append ("ref ");
5168 buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
5169 buf.Append (parameter.Name);
5170 if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant) {
5171 buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
5176 protected override string GetPropertyDeclaration (PropertyDefinition property)
5178 MethodDefinition method;
5180 string get_visible = null;
5181 if ((method = property.GetMethod) != null &&
5182 (DocUtils.IsExplicitlyImplemented (method) ||
5183 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5184 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5185 string set_visible = null;
5186 if ((method = property.SetMethod) != null &&
5187 (DocUtils.IsExplicitlyImplemented (method) ||
5188 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5189 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5191 if ((set_visible == null) && (get_visible == null))
5195 StringBuilder buf = new StringBuilder ();
5196 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
5197 buf.Append (visibility = get_visible);
5198 else if (set_visible != null && get_visible == null)
5199 buf.Append (visibility = set_visible);
5201 buf.Append (visibility = "public");
5203 // Pick an accessor to use for static/virtual/override/etc. checks.
5204 method = property.SetMethod;
5206 method = property.GetMethod;
5208 string modifiers = String.Empty;
5209 if (method.IsStatic) modifiers += " static";
5210 if (method.IsVirtual && !method.IsAbstract) {
5211 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
5212 modifiers += " virtual";
5214 modifiers += " override";
5216 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
5217 if (method.IsAbstract && !declDef.IsInterface)
5218 modifiers += " abstract";
5220 modifiers += " sealed";
5221 if (modifiers == " virtual sealed")
5223 buf.Append (modifiers).Append (' ');
5225 buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
5227 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
5228 string name = property.Name;
5229 foreach (MemberReference mi in defs) {
5230 if (mi == property) {
5235 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
5237 if (property.Parameters.Count != 0) {
5238 AppendParameters (buf, method, property.Parameters, '[', ']');
5242 if (get_visible != null) {
5243 if (get_visible != visibility)
5244 buf.Append (' ').Append (get_visible);
5245 buf.Append (" get;");
5247 if (set_visible != null) {
5248 if (set_visible != visibility)
5249 buf.Append (' ').Append (set_visible);
5250 buf.Append (" set;");
5254 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
5257 protected override string GetFieldDeclaration (FieldDefinition field)
5259 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
5260 if (declType.IsEnum && field.Name == "value__")
5261 return null; // This member of enums aren't documented.
5263 StringBuilder buf = new StringBuilder ();
5264 AppendFieldVisibility (buf, field);
5265 if (buf.Length == 0)
5268 if (declType.IsEnum)
5271 if (field.IsStatic && !field.IsLiteral)
5272 buf.Append (" static");
5273 if (field.IsInitOnly)
5274 buf.Append (" readonly");
5275 if (field.IsLiteral)
5276 buf.Append (" const");
5278 buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
5279 buf.Append (field.Name);
5280 AppendFieldValue (buf, field);
5283 return buf.ToString ();
5286 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
5289 return buf.Append ("public");
5290 if (field.IsFamily || field.IsFamilyOrAssembly)
5291 return buf.Append ("protected");
5295 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
5297 // enums have a value__ field, which we ignore
5298 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
5299 field.DeclaringType.IsGenericType ())
5301 if (field.HasConstant && field.IsLiteral) {
5304 val = field.Constant;
5309 buf.Append (" = ").Append ("null");
5310 else if (val is Enum)
5311 buf.Append (" = ").Append (val.ToString ());
5312 else if (val is IFormattable) {
5313 string value = ((IFormattable)val).ToString();
5315 value = "\"" + value + "\"";
5316 buf.Append (" = ").Append (value);
5322 protected override string GetEventDeclaration (EventDefinition e)
5324 StringBuilder buf = new StringBuilder ();
5325 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
5329 AppendModifiers (buf, e.AddMethod);
5331 buf.Append (" event ");
5332 buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters [0]))).Append (' ');
5333 buf.Append (e.Name).Append (';');
5335 return buf.ToString ();
5339 class CSharpMemberFormatter : CSharpFullMemberFormatter {
5340 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5346 class DocTypeFullMemberFormatter : MemberFormatter {
5347 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
5349 protected override char NestedTypeSeparator {
5354 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
5355 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5361 class SlashDocMemberFormatter : MemberFormatter {
5363 protected override char[] GenericTypeContainer {
5364 get {return new char[]{'{', '}'};}
5367 private bool AddTypeCount = true;
5369 private TypeReference genDeclType;
5370 private MethodReference genDeclMethod;
5372 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
5374 if (type is GenericParameter) {
5376 if (genDeclType != null) {
5377 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
5378 for (int i = 0; i < genArgs.Count; ++i) {
5379 if (genArgs [i].Name == type.Name) {
5380 buf.Append ('`').Append (i);
5385 if (genDeclMethod != null) {
5386 IList<GenericParameter> genArgs = null;
5387 if (genDeclMethod.IsGenericMethod ()) {
5388 genArgs = genDeclMethod.GenericParameters;
5389 for (int i = 0; i < genArgs.Count; ++i) {
5390 if (genArgs [i].Name == type.Name) {
5391 buf.Append ("``").Append (i);
5397 if (genDeclType == null && genDeclMethod == null) {
5398 // Probably from within an explicitly implemented interface member,
5399 // where CSC uses parameter names instead of indices (why?), e.g.
5400 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
5401 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
5402 buf.Append (type.Name);
5404 if (buf.Length == l) {
5405 throw new Exception (string.Format (
5406 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
5407 type.Name, genDeclType, genDeclMethod));
5411 base.AppendTypeName (buf, type, context);
5413 int numArgs = type.GenericParameters.Count;
5414 if (type.DeclaringType != null)
5415 numArgs -= type.GenericParameters.Count;
5417 buf.Append ('`').Append (numArgs);
5424 protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
5426 buf.Append (ArrayDelimeters [0]);
5427 int rank = array.Rank;
5430 for (int i = 1; i < rank; ++i) {
5434 return buf.Append (ArrayDelimeters [1]);
5437 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5440 base.AppendGenericType (buf, type, context);
5442 AppendType (buf, type, context);
5446 private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5448 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
5449 bool insertNested = false;
5450 int prevParamCount = 0;
5451 foreach (var decl in decls) {
5453 buf.Append (NestedTypeSeparator);
5454 insertNested = true;
5455 base.AppendTypeName (buf, decl, context);
5456 int argCount = DocUtils.GetGenericArgumentCount (decl);
5457 int numArgs = argCount - prevParamCount;
5458 prevParamCount = argCount;
5460 buf.Append ('`').Append (numArgs);
5465 public override string GetDeclaration (MemberReference member)
5467 TypeReference r = member as TypeReference;
5469 return "T:" + GetTypeName (r);
5471 return base.GetDeclaration (member);
5474 protected override string GetConstructorName (MethodReference constructor)
5476 return GetMethodDefinitionName (constructor, "#ctor");
5479 protected override string GetMethodName (MethodReference method)
5482 MethodDefinition methodDef = method as MethodDefinition;
5483 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
5486 TypeReference iface;
5487 MethodReference ifaceMethod;
5488 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
5489 AddTypeCount = false;
5490 name = GetTypeName (iface) + "." + ifaceMethod.Name;
5491 AddTypeCount = true;
5493 return GetMethodDefinitionName (method, name);
5496 private string GetMethodDefinitionName (MethodReference method, string name)
5498 StringBuilder buf = new StringBuilder ();
5499 buf.Append (GetTypeName (method.DeclaringType));
5501 buf.Append (name.Replace (".", "#"));
5502 if (method.IsGenericMethod ()) {
5503 IList<GenericParameter> genArgs = method.GenericParameters;
5504 if (genArgs.Count > 0)
5505 buf.Append ("``").Append (genArgs.Count);
5507 IList<ParameterDefinition> parameters = method.Parameters;
5509 genDeclType = method.DeclaringType;
5510 genDeclMethod = method;
5511 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
5515 genDeclMethod = null;
5517 return buf.ToString ();
5520 private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
5522 if (parameters.Count == 0)
5527 AppendParameter (buf, genArgs, parameters [0]);
5528 for (int i = 1; i < parameters.Count; ++i) {
5530 AppendParameter (buf, genArgs, parameters [i]);
5533 return buf.Append (')');
5536 private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
5538 AddTypeCount = false;
5539 buf.Append (GetTypeName (parameter.ParameterType));
5540 AddTypeCount = true;
5544 protected override string GetPropertyName (PropertyReference property)
5548 PropertyDefinition propertyDef = property as PropertyDefinition;
5549 MethodDefinition method = null;
5550 if (propertyDef != null)
5551 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
5552 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
5553 name = property.Name;
5555 TypeReference iface;
5556 MethodReference ifaceMethod;
5557 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5558 AddTypeCount = false;
5559 name = string.Join ("#", new string[]{
5560 GetTypeName (iface).Replace (".", "#"),
5561 DocUtils.GetMember (property.Name)
5563 AddTypeCount = true;
5566 StringBuilder buf = new StringBuilder ();
5567 buf.Append (GetName (property.DeclaringType));
5570 IList<ParameterDefinition> parameters = property.Parameters;
5571 if (parameters.Count > 0) {
5572 genDeclType = property.DeclaringType;
5574 IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
5575 AppendParameter (buf, genArgs, parameters [0]);
5576 for (int i = 1; i < parameters.Count; ++i) {
5578 AppendParameter (buf, genArgs, parameters [i]);
5583 return buf.ToString ();
5586 protected override string GetFieldName (FieldReference field)
5588 return string.Format ("{0}.{1}",
5589 GetName (field.DeclaringType), field.Name);
5592 protected override string GetEventName (EventReference e)
5594 return string.Format ("{0}.{1}",
5595 GetName (e.DeclaringType), e.Name);
5598 protected override string GetTypeDeclaration (TypeDefinition type)
5600 string name = GetName (type);
5606 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5608 string name = GetName (constructor);
5614 protected override string GetMethodDeclaration (MethodDefinition method)
5616 string name = GetName (method);
5619 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
5620 genDeclType = method.DeclaringType;
5621 genDeclMethod = method;
5622 name += "~" + GetName (method.ReturnType);
5624 genDeclMethod = null;
5629 protected override string GetPropertyDeclaration (PropertyDefinition property)
5631 string name = GetName (property);
5637 protected override string GetFieldDeclaration (FieldDefinition field)
5639 string name = GetName (field);
5645 protected override string GetEventDeclaration (EventDefinition e)
5647 string name = GetName (e);
5654 class FileNameMemberFormatter : SlashDocMemberFormatter {
5655 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5660 protected override char NestedTypeSeparator {